{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "negative-germany",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The autotime extension is already loaded. To reload it, use:\n",
      "  %reload_ext autotime\n",
      "time: 12.2 ms (started: 2021-08-18 23:00:21 +08:00)\n"
     ]
    }
   ],
   "source": [
    "# 自动计算cell的计算时间\n",
    "%load_ext autotime\n",
    "\n",
    "%matplotlib inline\n",
    "%config InlineBackend.figure_format='svg' #矢量图设置，让绘图更清晰"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "marked-claim",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "origin\tgit@github.com:ustchope/handson-ml2.git (fetch)\n",
      "origin\tgit@github.com:ustchope/handson-ml2.git (push)\n",
      "[master f9087be] 更新 ch14 #4  Aug 18, 2021\n",
      " 1 file changed, 1118 insertions(+), 156 deletions(-)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "To git@github.com:ustchope/handson-ml2.git\n",
      "   3b6927f..f9087be  master -> master\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 6.16 s (started: 2021-08-18 23:16:26 +08:00)\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "\n",
    "# 增加更新\n",
    "git add *.ipynb *.md\n",
    "\n",
    "git remote -v\n",
    "\n",
    "git commit -m '更新 ch14 #4  Aug 18, 2021'\n",
    "\n",
    "git push origin master"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "signal-brain",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 894 µs (started: 2021-08-18 23:00:23 +08:00)\n"
     ]
    }
   ],
   "source": [
    "#设置使用的gpu\n",
    "import tensorflow as tf\n",
    "\n",
    "gpus = tf.config.list_physical_devices(\"GPU\")\n",
    "\n",
    "if gpus:\n",
    "   \n",
    "    gpu0 = gpus[0] #如果有多个GPU，仅使用第0个GPU\n",
    "    tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量按需使用\n",
    "    # 或者也可以设置GPU显存为固定使用量(例如：4G)\n",
    "    #tf.config.experimental.set_virtual_device_configuration(gpu0,\n",
    "    #    [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=4096)]) \n",
    "    tf.config.set_visible_devices([gpu0],\"GPU\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "gross-jungle",
   "metadata": {},
   "source": [
    "# 使用卷积神经网络的深度计算机视觉"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "auburn-woman",
   "metadata": {},
   "source": [
    "尽管 IBM 的深蓝超级计算机早在 1996 年就击败了国际象棋世界冠军加里·卡斯帕罗夫，但直到最近，计算机才能够可靠地执行看似微不足道的任务，例如检测图片中的小狗或识别口语。 为什么这些任务对我们人类来说如此轻松？ 答案在于，感知主要发生在我们的意识领域之外，在我们大脑中专门的视觉、听觉和其他感官模块内。 当感官信息到达我们的意识时，它已经被高级特征所装饰； 例如，当你看一张可爱的小狗的照片时，你不能选择不看小狗，不注意它的可爱。 你也无法解释你是如何认出一只可爱的小狗的； 这对你来说很明显。 因此，我们不能相信我们的主观经验：感知根本不是微不足道的，要理解它，我们必须看看感官模块是如何工作的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "induced-mixer",
   "metadata": {},
   "source": [
    "卷积神经网络 (CNN) 起源于对大脑视觉皮层的研究，自 1980 年代以来一直用于图像识别。 在过去的几年里，由于计算能力的增加、可用训练数据的数量以及第 11 章中介绍的训练深度网络的技巧，CNN 已经在一些复杂的视觉任务上取得了超人的表现。 它们为图像搜索服务、自动驾驶汽车、自动视频分类系统等提供支持。 此外，CNN 不仅限于视觉感知：它们在许多其他任务上也很成功，例如语音识别和自然语言处理。 但是，我们现在将专注于可视化应用程序。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ordinary-trinidad",
   "metadata": {},
   "source": [
    "在本章中，我们将探讨 CNN 的来源、它们的构建块是什么样的，以及如何使用 TensorFlow 和 Keras 实现它们。 然后我们将讨论一些最好的 CNN 架构，以及其他视觉任务，包括对象检测（对图像中的多个对象进行分类并在它们周围放置边界框）和语义分割（根据对象的类别对每个像素进行分类） 属于）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "significant-celebrity",
   "metadata": {},
   "source": [
    "## 视觉皮层的架构"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "separate-tyler",
   "metadata": {},
   "source": [
    "David H. Hubel 和 Torsten Wiesel 在 1958 年和 1959 年（以及几年后在猴子身上）对猫进行了一系列实验，对视觉皮层的结构提供了重要的见解（作者获得了诺贝尔生理学或医学奖） 1981 年为他们的工作）。特别是，他们发现视觉皮层中的许多神经元有一个小的局部感受野，这意味着它们只对位于视野有限区域内的视觉刺激做出反应（见图 14-1，其中 5 个局部感受野）神经元用虚线圆圈表示）。不同神经元的感受野可能重叠，它们一起平铺整个视野。\n",
    "\n",
    "此外，作者表明，一些神经元仅对水平线的图像作出反应，而其他神经元仅对不同方向的线作出反应（两个神经元可能具有相同的感受野，但对不同的线方向作出反应）。他们还注意到一些神经元具有更大的感受野，它们对更复杂的模式做出反应，这些模式是较低级别模式的组合。这些观察导致了这样的想法：高级神经元基于相邻的低级神经元的输出（在图 14-1 中，请注意每个神经元仅连接到前一层的几个神经元）。这种强大的架构能够检测视野任何区域的各种复杂模式。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "mechanical-processing",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtkyd76lsmj61ak0meae602.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "monetary-davis",
   "metadata": {},
   "source": [
    "这些对视觉皮层的研究启发了新认知机，1980 年引入，逐渐演变成我们现在所说的卷积神经网络。 一个重要的里程碑是 Yann LeCun 等人 1998 年的一篇论文。 引入了著名的 LeNet-5 架构，被银行广泛用于识别手写支票号码。 这个架构有一些你已经知道的构建块，比如全连接层和 sigmoid 激活函数，但它也引入了两个新的构建块：卷积层和池化层。 现在让我们看看它们。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "spanish-process",
   "metadata": {},
   "source": [
    "> 为什么不简单地使用具有全连接层的深度神经网络来执行图像识别任务？ 不幸的是，尽管这对于小图像（例如 MNIST）来说效果很好，但由于需要大量参数，它对于较大的图像会失效。 例如，一张 100 × 100 像素的图像有 10,000 个像素，如果第一层只有 1,000 个神经元（这已经严重限制了传输到下一层的信息量），这意味着总共有 1000 万个连接。 这只是第一层。 CNN 使用部分连接的层和权重共享来解决这个问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "greater-liver",
   "metadata": {},
   "source": [
    "## 卷积层"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "curious-rover",
   "metadata": {},
   "source": [
    "CNN 最重要的构建块是卷积层：第一个卷积层中的神经元并没有连接到输入图像中的每个像素（就像它们在前几章讨论的层中一样），而仅连接到它们的接受像素 字段（见图 14-2）。 反过来，第二个卷积层中的每个神经元只连接到位于第一层小矩形内的神经元。 这种架构允许网络在第一个隐藏层专注于小的低级特征，然后在下一个隐藏层将它们组装成更大的更高级特征，依此类推。 这种层次结构在现实世界的图像中很常见，这也是 CNN 在图像识别方面表现如此出色的原因之一。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "grand-arrival",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtkyj3v3fdj619i0m6gnq02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "listed-wesley",
   "metadata": {},
   "source": [
    "> 到目前为止，我们看到的所有多层神经网络都有由一长串神经元组成的层，我们必须将输入图像展平为 1D，然后再将它们输入神经网络。 在 CNN 中，每一层都以 2D 形式表示，这使得将神经元与其相应的输入相匹配变得更容易。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "moved-genre",
   "metadata": {},
   "source": [
    "位于给定层第 i 行、第 j 列的神经元连接到位于第 i 行到 i + fh – 1、第 j 列到 j + fw – 1 行的前一层神经元的输出，其中 fh 和 fw 是 感受野的高度和宽度（见图 14 3）。 为了使图层具有与前一层相同的高度和宽度，通常在输入周围添加零，如图所示。 这称为零填充。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "political-brunswick",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtkyllbfdgj618u0q6tc202.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "loved-creator",
   "metadata": {},
   "source": [
    "还可以通过将感受野隔开来将一个大的输入层连接到一个小得多的层，如图 14-4 所示。 这大大降低了模型的计算复杂度。 从一个感受野到下一个感受野的转变称为步幅。 在图中，一个 5 × 7 的输入层（加上零填充）连接到一个 3 × 4 的层，使用 3 × 3 的感受野和 2 的步幅（在这个例子中，两个方向的步幅相同，但它 不必如此）。 位于上层第 i 行、第 j 列的神经元连接到位于第 $i × s_h$ 至 $i × s_h + f_h – 1$、第 $j × s_w$ 至 $j × s_w + f_w$ 列的上一层神经元的输出 – 1，其中 $s_h$ 和 $s_w$ 是垂直和水平步幅。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "higher-survey",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtkyninpq4j619c0o4ad202.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "suburban-relations",
   "metadata": {},
   "source": [
    "### 卷积核"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "square-california",
   "metadata": {},
   "source": [
    "神经元的权重可以表示为感受野大小的小图像。 例如，图 14-5 显示了两组可能的权重，称为过滤器（或卷积核）。 第一个表示为一个黑色方块，中间有一条垂直的白线（它是一个 7 × 7 的矩阵，除了中央一列全是 1 之外，全是 0）； 使用这些权重的神经元将忽略其感受野中除中央垂直线之外的所有内容（因为所有输入都将乘以 0，除了位于中央垂直线上的输入）。 第二个过滤器是一个黑色方块，中间有一条水平白线。 再一次，使用这些权重的神经元将忽略其感受野中除中央水平线之外的所有内容。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "enabling-pacific",
   "metadata": {},
   "source": [
    "现在，如果一层中的所有神经元都使用相同的垂直线滤波器（和相同的偏置项），并且您将图 14-5 所示的输入图像（底部图像）提供给网络，则该层将输出左上角的图像 . 请注意，垂直白线得到增强，而其余部分变得模糊。 类似地，如果所有神经元都使用相同的水平线过滤器，则您会得到右上方的图像； 请注意，水平白线得到增强，而其余部分变得模糊。 因此，一个充满神经元的层使用相同的过滤器输出一个特征图，它突出显示图像中激活过滤器最多的区域。 当然，您不必手动定义过滤器：相反，在训练期间，卷积层将自动学习对其任务最有用的过滤器，并且上面的层将学习将它们组合成更复杂的模式。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "liquid-democrat",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtkyqx80hdj618z0u0td502.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "processed-emphasis",
   "metadata": {},
   "source": [
    "### 堆叠多个特征图"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sporting-smart",
   "metadata": {},
   "source": [
    "到现在为止，为了简单起见，我已经将每个卷积层的输出表示为一个 2D 层，但实际上一个卷积层有多个过滤器（您决定多少）并且每个过滤器输出一个特征图，因此更准确地表示 在 3D 中（见图 14-6）。 它在每个特征图中每个像素有一个神经元，并且给定特征图中的所有神经元共享相同的参数（即相同的权重和偏置项）。 不同特征图中的神经元使用不同的参数。 神经元的感受野与前面描述的相同，但它扩展到所有先前层的特征图。 简而言之，卷积层同时将多个可训练滤波器应用于其输入，使其能够在其输入的任何位置检测多个特征。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "unexpected-insertion",
   "metadata": {},
   "source": [
    "特征图中的所有神经元共享相同的参数这一事实大大减少了模型中的参数数量。 一旦 CNN 学会了在一个位置识别模式，它就可以在任何其他位置识别它。 相比之下，一旦常规 DNN 学会了在一个位置识别模式，它只能在该特定位置识别它。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "designing-calvin",
   "metadata": {},
   "source": [
    "输入图像也由多个子层组成：每个颜色通道一个。 通常有三种：红色、绿色和蓝色 (RGB)。 灰度图像只有一个通道，但有些图像可能有更多通道——例如，捕获额外光频率（如红外线）的卫星图像。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "listed-spanish",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtkytksmnkj60pw0o6acq02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "early-collectible",
   "metadata": {},
   "source": [
    "具体来说，位于给定卷积层 l 中特征图 k 的第 i 行第 j 列的神经元连接到前一层 $l – 1$ 中位于第 $i × s_h$ 到 $i × s_h + f_h$ 行的神经元的输出 – 1 列 $j × s_w$ 到 $j × s_w + f_w – 1$，跨越所有特征图（在第 $l – 1$ 层）。 请注意，位于同一行 i 和列 j 但在不同特征图中的所有神经元都连接到前一层中完全相同的神经元的输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "lyric-familiar",
   "metadata": {},
   "source": [
    "公式 14-1 在一个大的数学公式中总结了前面的解释：它显示了如何计算卷积层中给定神经元的输出。 由于所有不同的索引，它有点难看，但它所做的只是计算所有输入的加权和，加上偏差项。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "metric-constitution",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtkz42dytpj616408g75802.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dental-monitoring",
   "metadata": {},
   "source": [
    "在这个等式中：\n",
    "\n",
    "* $z_{i, j, k}$ 是位于卷积层（第l 层）的特征图k 中第i 行第j 列的神经元的输出。\n",
    "* 如前所述，$s_h$ 和$s_w$ 是垂直和水平步长，$f_h$ 和$f_w$ 是感受野的高度和宽度，$f_{n'}$ 是前一层（层$l – 1$）中的特征图的数量。\n",
    "* $x_{i', j', k'}$ 是位于第 $l – 1$ 层，第 $i'$ 行，第 $j'$ 列，特征图 $k'$（如果前一层是输入层，则为通道 $k'$）中神经元的输出。\n",
    "* $b_k$ 是特征图k（在第l 层）的偏置项。 您可以将其视为调整特征图 k 整体亮度的旋钮。 \n",
    "* $w_{u, v, k′ ,k}$ 是第 $l$ 层的特征图 $k$ 中的任何神经元与其位于第 $u$ 行、第 $v$ 列（相对于神经元的感受野）和特征图 $k′$ 的输入之间的连接权重。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "binding-damage",
   "metadata": {},
   "source": [
    "### TensorFlow 实现"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "handled-complement",
   "metadata": {},
   "source": [
    "在 TensorFlow 中，每个输入图像通常表示为形状 [高度、宽度、通道] 的 3D 张量。 小批量表示为形状 [小批量大小、高度、宽度、通道] 的 4D 张量。 卷积层的权重表示为形状为 [$f_h, f_w, f_{n'}, f_n$] 的 4D 张量。 卷积层的偏置项简单地表示为形状为 [$f_n$] 的一维张量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aging-sheet",
   "metadata": {},
   "source": [
    "我们来看一个简单的例子。 下面的代码使用 Scikit-Learn 的 `load_sample_image()` 加载两个示例图像（加载两个彩色图像，一个是中国寺庙，另一个是花），然后创建两个过滤器并将它们应用于两个图像，最后 它显示生成的特征图之一。 请注意，您必须通过 `pip` 安装 `Pillow` 包才能使用 `load_sample_image()`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "exposed-deviation",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 1.16 s (started: 2021-08-18 15:38:29 +08:00)\n"
     ]
    }
   ],
   "source": [
    "from sklearn.datasets import load_sample_image\n",
    "\n",
    "# Load sample images\n",
    "china = load_sample_image(\"china.jpg\") / 255\n",
    "flower = load_sample_image(\"flower.jpg\") / 255"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "aware-oxford",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(427, 640, 3)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 2.66 ms (started: 2021-08-18 15:39:09 +08:00)\n"
     ]
    }
   ],
   "source": [
    "china.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "going-saskatchewan",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 13.5 ms (started: 2021-08-18 15:41:16 +08:00)\n"
     ]
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "#plt.imshow(china)\n",
    "\n",
    "images = np.array([china, flower])\n",
    "\n",
    "batch_size, height, width, channels = images.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "vertical-manual",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 427, 640, 3)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 2.99 ms (started: 2021-08-18 15:41:30 +08:00)\n"
     ]
    }
   ],
   "source": [
    "images.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "desperate-stadium",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       "  \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<svg height=\"252.06273pt\" version=\"1.1\" viewBox=\"0 0 366.392886 252.06273\" width=\"366.392886pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       " <metadata>\n",
       "  <rdf:RDF xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n",
       "   <cc:Work>\n",
       "    <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\"/>\n",
       "    <dc:date>2021-08-18T15:43:10.409758</dc:date>\n",
       "    <dc:format>image/svg+xml</dc:format>\n",
       "    <dc:creator>\n",
       "     <cc:Agent>\n",
       "      <dc:title>Matplotlib v3.4.2, https://matplotlib.org/</dc:title>\n",
       "     </cc:Agent>\n",
       "    </dc:creator>\n",
       "   </cc:Work>\n",
       "  </rdf:RDF>\n",
       " </metadata>\n",
       " <defs>\n",
       "  <style type=\"text/css\">*{stroke-linecap:butt;stroke-linejoin:round;}</style>\n",
       " </defs>\n",
       " <g id=\"figure_1\">\n",
       "  <g id=\"patch_1\">\n",
       "   <path d=\"M 0 252.06273 \n",
       "L 366.392886 252.06273 \n",
       "L 366.392886 0 \n",
       "L 0 0 \n",
       "z\n",
       "\" style=\"fill:none;\"/>\n",
       "  </g>\n",
       "  <g id=\"axes_1\">\n",
       "   <g id=\"patch_2\">\n",
       "    <path d=\"M 33.2875 228.184605 \n",
       "L 359.192886 228.184605 \n",
       "L 359.192886 10.744605 \n",
       "L 33.2875 10.744605 \n",
       "z\n",
       "\" style=\"fill:#ffffff;\"/>\n",
       "   </g>\n",
       "   <g clip-path=\"url(#p519f3d554a)\">\n",
       "    <image height=\"218\" id=\"image1a4cfc2709\" transform=\"scale(1 -1)translate(0 -218)\" width=\"326\" x=\"33.2875\" xlink:href=\"data:image/png;base64,\n",
       "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\" y=\"-10.184605\"/>\n",
       "   </g>\n",
       "   <g id=\"matplotlib.axis_1\">\n",
       "    <g id=\"xtick_1\">\n",
       "     <g id=\"line2d_1\">\n",
       "      <defs>\n",
       "       <path d=\"M 0 0 \n",
       "L 0 3.5 \n",
       "\" id=\"m38a866eb32\" style=\"stroke:#000000;stroke-width:0.8;\"/>\n",
       "      </defs>\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.542114\" xlink:href=\"#m38a866eb32\" y=\"228.184605\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_1\">\n",
       "      <!-- 0 -->\n",
       "      <g transform=\"translate(30.360864 242.783043)scale(0.1 -0.1)\">\n",
       "       <defs>\n",
       "        <path d=\"M 2034 4250 \n",
       "Q 1547 4250 1301 3770 \n",
       "Q 1056 3291 1056 2328 \n",
       "Q 1056 1369 1301 889 \n",
       "Q 1547 409 2034 409 \n",
       "Q 2525 409 2770 889 \n",
       "Q 3016 1369 3016 2328 \n",
       "Q 3016 3291 2770 3770 \n",
       "Q 2525 4250 2034 4250 \n",
       "z\n",
       "M 2034 4750 \n",
       "Q 2819 4750 3233 4129 \n",
       "Q 3647 3509 3647 2328 \n",
       "Q 3647 1150 3233 529 \n",
       "Q 2819 -91 2034 -91 \n",
       "Q 1250 -91 836 529 \n",
       "Q 422 1150 422 2328 \n",
       "Q 422 3509 836 4129 \n",
       "Q 1250 4750 2034 4750 \n",
       "z\n",
       "\" id=\"DejaVuSans-30\" transform=\"scale(0.015625)\"/>\n",
       "       </defs>\n",
       "       <use xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"xtick_2\">\n",
       "     <g id=\"line2d_2\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"84.46483\" xlink:href=\"#m38a866eb32\" y=\"228.184605\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_2\">\n",
       "      <!-- 100 -->\n",
       "      <g transform=\"translate(74.92108 242.783043)scale(0.1 -0.1)\">\n",
       "       <defs>\n",
       "        <path d=\"M 794 531 \n",
       "L 1825 531 \n",
       "L 1825 4091 \n",
       "L 703 3866 \n",
       "L 703 4441 \n",
       "L 1819 4666 \n",
       "L 2450 4666 \n",
       "L 2450 531 \n",
       "L 3481 531 \n",
       "L 3481 0 \n",
       "L 794 0 \n",
       "L 794 531 \n",
       "z\n",
       "\" id=\"DejaVuSans-31\" transform=\"scale(0.015625)\"/>\n",
       "       </defs>\n",
       "       <use xlink:href=\"#DejaVuSans-31\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"xtick_3\">\n",
       "     <g id=\"line2d_3\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"135.387547\" xlink:href=\"#m38a866eb32\" y=\"228.184605\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_3\">\n",
       "      <!-- 200 -->\n",
       "      <g transform=\"translate(125.843797 242.783043)scale(0.1 -0.1)\">\n",
       "       <defs>\n",
       "        <path d=\"M 1228 531 \n",
       "L 3431 531 \n",
       "L 3431 0 \n",
       "L 469 0 \n",
       "L 469 531 \n",
       "Q 828 903 1448 1529 \n",
       "Q 2069 2156 2228 2338 \n",
       "Q 2531 2678 2651 2914 \n",
       "Q 2772 3150 2772 3378 \n",
       "Q 2772 3750 2511 3984 \n",
       "Q 2250 4219 1831 4219 \n",
       "Q 1534 4219 1204 4116 \n",
       "Q 875 4013 500 3803 \n",
       "L 500 4441 \n",
       "Q 881 4594 1212 4672 \n",
       "Q 1544 4750 1819 4750 \n",
       "Q 2544 4750 2975 4387 \n",
       "Q 3406 4025 3406 3419 \n",
       "Q 3406 3131 3298 2873 \n",
       "Q 3191 2616 2906 2266 \n",
       "Q 2828 2175 2409 1742 \n",
       "Q 1991 1309 1228 531 \n",
       "z\n",
       "\" id=\"DejaVuSans-32\" transform=\"scale(0.015625)\"/>\n",
       "       </defs>\n",
       "       <use xlink:href=\"#DejaVuSans-32\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"xtick_4\">\n",
       "     <g id=\"line2d_4\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"186.310263\" xlink:href=\"#m38a866eb32\" y=\"228.184605\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_4\">\n",
       "      <!-- 300 -->\n",
       "      <g transform=\"translate(176.766513 242.783043)scale(0.1 -0.1)\">\n",
       "       <defs>\n",
       "        <path d=\"M 2597 2516 \n",
       "Q 3050 2419 3304 2112 \n",
       "Q 3559 1806 3559 1356 \n",
       "Q 3559 666 3084 287 \n",
       "Q 2609 -91 1734 -91 \n",
       "Q 1441 -91 1130 -33 \n",
       "Q 819 25 488 141 \n",
       "L 488 750 \n",
       "Q 750 597 1062 519 \n",
       "Q 1375 441 1716 441 \n",
       "Q 2309 441 2620 675 \n",
       "Q 2931 909 2931 1356 \n",
       "Q 2931 1769 2642 2001 \n",
       "Q 2353 2234 1838 2234 \n",
       "L 1294 2234 \n",
       "L 1294 2753 \n",
       "L 1863 2753 \n",
       "Q 2328 2753 2575 2939 \n",
       "Q 2822 3125 2822 3475 \n",
       "Q 2822 3834 2567 4026 \n",
       "Q 2313 4219 1838 4219 \n",
       "Q 1578 4219 1281 4162 \n",
       "Q 984 4106 628 3988 \n",
       "L 628 4550 \n",
       "Q 988 4650 1302 4700 \n",
       "Q 1616 4750 1894 4750 \n",
       "Q 2613 4750 3031 4423 \n",
       "Q 3450 4097 3450 3541 \n",
       "Q 3450 3153 3228 2886 \n",
       "Q 3006 2619 2597 2516 \n",
       "z\n",
       "\" id=\"DejaVuSans-33\" transform=\"scale(0.015625)\"/>\n",
       "       </defs>\n",
       "       <use xlink:href=\"#DejaVuSans-33\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"xtick_5\">\n",
       "     <g id=\"line2d_5\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"237.23298\" xlink:href=\"#m38a866eb32\" y=\"228.184605\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_5\">\n",
       "      <!-- 400 -->\n",
       "      <g transform=\"translate(227.68923 242.783043)scale(0.1 -0.1)\">\n",
       "       <defs>\n",
       "        <path d=\"M 2419 4116 \n",
       "L 825 1625 \n",
       "L 2419 1625 \n",
       "L 2419 4116 \n",
       "z\n",
       "M 2253 4666 \n",
       "L 3047 4666 \n",
       "L 3047 1625 \n",
       "L 3713 1625 \n",
       "L 3713 1100 \n",
       "L 3047 1100 \n",
       "L 3047 0 \n",
       "L 2419 0 \n",
       "L 2419 1100 \n",
       "L 313 1100 \n",
       "L 313 1709 \n",
       "L 2253 4666 \n",
       "z\n",
       "\" id=\"DejaVuSans-34\" transform=\"scale(0.015625)\"/>\n",
       "       </defs>\n",
       "       <use xlink:href=\"#DejaVuSans-34\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"xtick_6\">\n",
       "     <g id=\"line2d_6\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"288.155697\" xlink:href=\"#m38a866eb32\" y=\"228.184605\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_6\">\n",
       "      <!-- 500 -->\n",
       "      <g transform=\"translate(278.611947 242.783043)scale(0.1 -0.1)\">\n",
       "       <defs>\n",
       "        <path d=\"M 691 4666 \n",
       "L 3169 4666 \n",
       "L 3169 4134 \n",
       "L 1269 4134 \n",
       "L 1269 2991 \n",
       "Q 1406 3038 1543 3061 \n",
       "Q 1681 3084 1819 3084 \n",
       "Q 2600 3084 3056 2656 \n",
       "Q 3513 2228 3513 1497 \n",
       "Q 3513 744 3044 326 \n",
       "Q 2575 -91 1722 -91 \n",
       "Q 1428 -91 1123 -41 \n",
       "Q 819 9 494 109 \n",
       "L 494 744 \n",
       "Q 775 591 1075 516 \n",
       "Q 1375 441 1709 441 \n",
       "Q 2250 441 2565 725 \n",
       "Q 2881 1009 2881 1497 \n",
       "Q 2881 1984 2565 2268 \n",
       "Q 2250 2553 1709 2553 \n",
       "Q 1456 2553 1204 2497 \n",
       "Q 953 2441 691 2322 \n",
       "L 691 4666 \n",
       "z\n",
       "\" id=\"DejaVuSans-35\" transform=\"scale(0.015625)\"/>\n",
       "       </defs>\n",
       "       <use xlink:href=\"#DejaVuSans-35\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"xtick_7\">\n",
       "     <g id=\"line2d_7\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"339.078413\" xlink:href=\"#m38a866eb32\" y=\"228.184605\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_7\">\n",
       "      <!-- 600 -->\n",
       "      <g transform=\"translate(329.534663 242.783043)scale(0.1 -0.1)\">\n",
       "       <defs>\n",
       "        <path d=\"M 2113 2584 \n",
       "Q 1688 2584 1439 2293 \n",
       "Q 1191 2003 1191 1497 \n",
       "Q 1191 994 1439 701 \n",
       "Q 1688 409 2113 409 \n",
       "Q 2538 409 2786 701 \n",
       "Q 3034 994 3034 1497 \n",
       "Q 3034 2003 2786 2293 \n",
       "Q 2538 2584 2113 2584 \n",
       "z\n",
       "M 3366 4563 \n",
       "L 3366 3988 \n",
       "Q 3128 4100 2886 4159 \n",
       "Q 2644 4219 2406 4219 \n",
       "Q 1781 4219 1451 3797 \n",
       "Q 1122 3375 1075 2522 \n",
       "Q 1259 2794 1537 2939 \n",
       "Q 1816 3084 2150 3084 \n",
       "Q 2853 3084 3261 2657 \n",
       "Q 3669 2231 3669 1497 \n",
       "Q 3669 778 3244 343 \n",
       "Q 2819 -91 2113 -91 \n",
       "Q 1303 -91 875 529 \n",
       "Q 447 1150 447 2328 \n",
       "Q 447 3434 972 4092 \n",
       "Q 1497 4750 2381 4750 \n",
       "Q 2619 4750 2861 4703 \n",
       "Q 3103 4656 3366 4563 \n",
       "z\n",
       "\" id=\"DejaVuSans-36\" transform=\"scale(0.015625)\"/>\n",
       "       </defs>\n",
       "       <use xlink:href=\"#DejaVuSans-36\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "   </g>\n",
       "   <g id=\"matplotlib.axis_2\">\n",
       "    <g id=\"ytick_1\">\n",
       "     <g id=\"line2d_8\">\n",
       "      <defs>\n",
       "       <path d=\"M 0 0 \n",
       "L -3.5 0 \n",
       "\" id=\"mf8d7a324e3\" style=\"stroke:#000000;stroke-width:0.8;\"/>\n",
       "      </defs>\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"10.999219\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_8\">\n",
       "      <!-- 0 -->\n",
       "      <g transform=\"translate(19.925 14.798438)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_2\">\n",
       "     <g id=\"line2d_9\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"36.460577\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_9\">\n",
       "      <!-- 50 -->\n",
       "      <g transform=\"translate(13.5625 40.259796)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-35\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_3\">\n",
       "     <g id=\"line2d_10\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"61.921935\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_10\">\n",
       "      <!-- 100 -->\n",
       "      <g transform=\"translate(7.2 65.721154)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-31\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_4\">\n",
       "     <g id=\"line2d_11\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"87.383294\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_11\">\n",
       "      <!-- 150 -->\n",
       "      <g transform=\"translate(7.2 91.182512)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-31\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-35\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_5\">\n",
       "     <g id=\"line2d_12\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"112.844652\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_12\">\n",
       "      <!-- 200 -->\n",
       "      <g transform=\"translate(7.2 116.643871)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-32\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_6\">\n",
       "     <g id=\"line2d_13\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"138.30601\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_13\">\n",
       "      <!-- 250 -->\n",
       "      <g transform=\"translate(7.2 142.105229)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-32\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-35\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_7\">\n",
       "     <g id=\"line2d_14\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"163.767369\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_14\">\n",
       "      <!-- 300 -->\n",
       "      <g transform=\"translate(7.2 167.566587)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-33\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_8\">\n",
       "     <g id=\"line2d_15\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"189.228727\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_15\">\n",
       "      <!-- 350 -->\n",
       "      <g transform=\"translate(7.2 193.027946)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-33\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-35\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "    <g id=\"ytick_9\">\n",
       "     <g id=\"line2d_16\">\n",
       "      <g>\n",
       "       <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"33.2875\" xlink:href=\"#mf8d7a324e3\" y=\"214.690085\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "     <g id=\"text_16\">\n",
       "      <!-- 400 -->\n",
       "      <g transform=\"translate(7.2 218.489304)scale(0.1 -0.1)\">\n",
       "       <use xlink:href=\"#DejaVuSans-34\"/>\n",
       "       <use x=\"63.623047\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "       <use x=\"127.246094\" xlink:href=\"#DejaVuSans-30\"/>\n",
       "      </g>\n",
       "     </g>\n",
       "    </g>\n",
       "   </g>\n",
       "   <g id=\"patch_3\">\n",
       "    <path d=\"M 33.2875 228.184605 \n",
       "L 33.2875 10.744605 \n",
       "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n",
       "   </g>\n",
       "   <g id=\"patch_4\">\n",
       "    <path d=\"M 359.192886 228.184605 \n",
       "L 359.192886 10.744605 \n",
       "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n",
       "   </g>\n",
       "   <g id=\"patch_5\">\n",
       "    <path d=\"M 33.2875 228.184605 \n",
       "L 359.192886 228.184605 \n",
       "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n",
       "   </g>\n",
       "   <g id=\"patch_6\">\n",
       "    <path d=\"M 33.2875 10.744605 \n",
       "L 359.192886 10.744605 \n",
       "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n",
       "   </g>\n",
       "  </g>\n",
       " </g>\n",
       " <defs>\n",
       "  <clipPath id=\"p519f3d554a\">\n",
       "   <rect height=\"217.44\" width=\"325.905386\" x=\"33.2875\" y=\"10.744605\"/>\n",
       "  </clipPath>\n",
       " </defs>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 7.47 s (started: 2021-08-18 15:43:03 +08:00)\n"
     ]
    }
   ],
   "source": [
    "# Create 2 filters\n",
    "filters = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)\n",
    "\n",
    "filters[:, 3, :, 0] = 1 # vertical line\n",
    "filters[3, :, :, 1] = 1 # horizontal line\n",
    "outputs = tf.nn.conv2d(images, filters, strides=1, padding=\"SAME\")\n",
    "plt.imshow(outputs[0, :, :, 1], cmap=\"gray\") # plot 1st image's 2nd feature map\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "downtown-particular",
   "metadata": {},
   "source": [
    "让我们来看看这段代码：\n",
    "* 每个颜色通道的像素强度表示为从 0 到 255 的一个字节，因此我们只需通过除以 255 来缩放这些特征，以获得范围从 0 到 1 的浮点数。\n",
    "* 然后我们创建两个 7 × 7 过滤器（一个中间有一条垂直的白线，另一个中间有一条水平的白线）。\n",
    "* 我们使用 tf.nn.conv2d() 函数将它们应用于两个图像，该函数是 TensorFlow 的低级深度学习 API 的一部分。 在这个例子中，我们使用零填充（padding=\"SAME\"）和 1 的步幅。\n",
    "* 最后，我们绘制生成的特征图之一（类似于图 14-5 中的右上角图像）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "automotive-festival",
   "metadata": {},
   "source": [
    "tf.nn.conv2d() 行值得更多解释：\n",
    "* 图像是输入小批量（4D 张量，如前所述）。\n",
    "* 过滤器是要应用的一组过滤器（也是 4D 张量，如前所述）。\n",
    "* strides 等于1，但它也可以是具有四个元素的一维数组，其中两个中心元素是垂直和水平步长（$s_h$ 和$s_w$）。第一个和最后一个元素当前必须等于 1。它们有一天可能用于指定批处理步幅（跳过某些实例）和通道步幅（跳过一些前一层的特征图或通道）。\n",
    "* padding \n",
    "    - 必须是“SAME”或“VALID”：——如果设置为“SAME”，卷积层在必要时使用零填充。输出大小设置为输入神经元的数量除以步幅，四舍五入。例如，如果输入大小为 13，步长为 5（见图 14-7），则输出大小为 3（即 13 / 5 = 2.6，四舍五入为 3）。然后在输入周围尽可能均匀地添加零，根据需要当 strides=1 时，层的输出将具有与其输入相同的空间维度（宽度和高度），因此名称相同。\n",
    "    - 如果设置为“VALID”，则卷积层不使用零填充，可能会忽略输入图像底部和右侧的一些行和列，具体取决于步幅，如图 14-7 所示（为简单起见，仅 此处显示的是水平维度，但当然相同的逻辑适用于垂直维度）。 这意味着每个神经元的感受野都严格位于输入内部的有效位置（它不会越界），因此名称有效。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "annual-teaching",
   "metadata": {},
   "source": [
    "在这个例子中，我们手动定义了过滤器，但在真正的 CNN 中，您通常会将过滤器定义为可训练变量，以便神经网络可以了解哪些过滤器效果最好，如前所述。 使用 keras.layers.Conv2D 层，而不是手动创建变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "according-packing",
   "metadata": {},
   "outputs": [],
   "source": [
    "conv = keras.layers.Conv2D(filters=32, \n",
    "                           kernel_size=3, \n",
    "                           strides=1,\n",
    "                           padding=\"same\", \n",
    "                           activation=\"relu\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "electronic-southeast",
   "metadata": {},
   "source": [
    "这段代码创建了一个包含 32 个过滤器的 Conv2D 层，每个过滤器为 3 × 3，使用 1 步长（水平和垂直）和“相同”填充，并将 ReLU 激活函数应用于其输出。 如您所见，卷积层有很多超参数：您必须选择过滤器的数量、高度和宽度、步幅和填充类型。 与往常一样，您可以使用交叉验证来找到正确的超参数值，但这非常耗时。 我们稍后将讨论常见的 CNN 架构，让您了解哪些超参数值在实践中效果最佳。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "processed-humidity",
   "metadata": {},
   "source": [
    "### 内存要求"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "british-husband",
   "metadata": {},
   "source": [
    "CNN 的另一个问题是卷积层需要大量 RAM。在训练期间尤其如此，因为反向传播的反向传播需要在正向传播期间计算的所有中间值。\n",
    "\n",
    "例如，考虑一个具有 5 × 5 滤波器的卷积层，输出 200 个大小为 150 × 100、步幅为 1 和“相同”填充的特征图。如果输入是 150 × 100 RGB 图像（三个通道），则参数数量为 (5 × 5 × 3 + 1) × 200 = 15,200（+1 对应于偏差项），相比之下相当小7 然而，200 个特征图中的每一个都包含 150 × 100 个神经元，每个神经元都需要计算其 5 × 5 × 3 = 75 个输入的加权和：总共 2.25 亿个浮点数乘法。不像全连接层那么糟糕，但仍然是计算密集型的。此外，如果特征图使用 32 位浮点数表示，那么卷积层的输出将占用 200 × 150 × 100 × 32 = 9600 万位（12 MB）的 RAM。这只是一个例子——如果训练批处理包含 100 个实例，那么这一层将使用 1.2 GB 的 RAM！"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "mighty-simulation",
   "metadata": {},
   "source": [
    "在推理期间（即对新实例进行预测时），只要计算下一层，就可以释放一层占用的 RAM，因此您只需要两个连续层所需的 RAM。 但是在训练期间，前向传递期间计算的所有内容都需要保留以供反向传递使用，因此所需的 RAM 量（至少）是所有层所需的 RAM 总量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "gothic-small",
   "metadata": {},
   "source": [
    "如果训练因内存不足错误而崩溃，您可以尝试减小小批量大小。 或者，您可以尝试使用步幅降低维度，或删除几层。 或者您可以尝试使用 16 位浮点数而不是 32 位浮点数。 或者您可以将 CNN 分布在多个设备上。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "psychological-burden",
   "metadata": {},
   "source": [
    "现在让我们看看 CNN 的第二个常见构建块：池化层。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "nonprofit-sport",
   "metadata": {},
   "source": [
    "## 池化层"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "through-column",
   "metadata": {},
   "source": [
    "一旦你理解了卷积层的工作原理，池化层就很容易掌握了。 他们的目标是对输入图像进行二次采样（即缩小），以减少计算负载、内存使用和参数数量（从而限制过拟合的风险）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "neural-patent",
   "metadata": {},
   "source": [
    "就像在卷积层中一样，池化层中的每个神经元都连接到前一层中有限数量的神经元的输出，这些神经元位于一个小的矩形感受野内。 您必须像以前一样定义它的大小、步幅和填充类型。 然而，池化神经元没有权重。 它所做的只是使用聚合函数（例如最大值或平均值）聚合输入。 图 14-8 显示了一个最大池化层，它是最常见的池化层类型。 在这个例子中，我们使用一个 2 × 2 的池化内核，步长为 2，没有填充。 只有每个感受野中的最大输入值才能进入下一层，而其他输入则被丢弃。 例如，在图 14-8 左下方的感受野中，输入值为 1、5、3、2，因此只有最大值 5 传播到下一层。 由于步幅为 2，输出图像具有输入图像的一半高度和一半宽度（由于我们不使用填充，因此向下取整）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "recreational-newsletter",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl033p6evj61ae0j0jv902.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "right-intelligence",
   "metadata": {},
   "source": [
    "池化层通常独立地作用于每个输入通道，因此输出深度与输入深度相同。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pleasant-professional",
   "metadata": {},
   "source": [
    "除了减少计算、内存使用和参数数量之外，最大池化层还为小翻译引入了一定程度的不变性，如图 14-9 所示。这里我们假设亮像素的值低于暗像素，我们考虑三个图像（A、B、C）通过一个最大池化层，内核大小为 2 × 2，步长为 2。图像 B 和 C 是相同的如图像 A，但向右移动了一个和两个像素。如您所见，图像 A 和 B 的最大池化层的输出是相同的。这就是平移不变性的意思。对于图像 C，输出是不同的：它向右移动一个像素（但仍然有 75% 的不变性）。通过在 CNN 中每隔几层插入一个最大池化层，就有可能在更大范围内获得某种程度的平移不变性。此外，最大池化提供了少量的旋转不变性和轻微的尺度不变性。这种不变性（即使它是有限的）在预测不应该依赖于这些细节的情况下很有用，例如在分类任务中。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "imported-chase",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl05b0i56j615m0oowh502.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "thick-lawyer",
   "metadata": {},
   "source": [
    "然而，最大池化也有一些缺点。 首先，它显然是非常具有破坏性的：即使使用很小的 2 × 2 内核和 2 步长，输出在两个方向上都会小两倍（因此它的面积会小四倍），只需丢弃 75% 的输入 值。 在某些应用程序中，不变性是不可取的。 以语义分割（根据像素所属的对象对图像中的每个像素进行分类的任务，我们将在本章稍后探讨）：显然，如果输入图像向右平移一个像素，则输出 也应该向右平移一个像素。 这种情况下的目标是等方差，而不是不变性：输入的微小变化应该导致输出的相应微小变化。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "mobile-angel",
   "metadata": {},
   "source": [
    "### TensorFlow 实现"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "equipped-cleaners",
   "metadata": {},
   "source": [
    "在 TensorFlow 中实现最大池化层非常简单。 以下代码使用 2 × 2 内核创建最大池化层。 步幅默认为内核大小，因此该层将使用步幅为 2（水平和垂直）。 默认情况下，它使用“有效”填充（即，根本没有填充）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "theoretical-modem",
   "metadata": {},
   "outputs": [],
   "source": [
    "max_pool = keras.layers.MaxPool2D(pool_size=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dimensional-advancement",
   "metadata": {},
   "source": [
    "要创建平均池化层，只需使用 AvgPool2D 而不是 MaxPool2D。 正如您所料，它的工作原理与最大池化层完全相同，只是它计算平均值而不是最大值。 平均池化层曾经非常流行，但现在人们大多使用最大池化层，因为它们通常表现更好。 这似乎令人惊讶，因为计算平均值通常比计算最大值丢失的信息少。 但另一方面，最大池化只保留最强的特征，去掉所有无意义的特征，因此下一层得到更清晰的信号。 此外，最大池化提供比平均池化更强的平移不变性，并且它需要的计算量略少。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "agreed-afternoon",
   "metadata": {},
   "source": [
    "请注意，最大池化和平均池化可以沿深度维度而不是空间维度执行，尽管这并不常见。 这可以让 CNN 学会对各种特征保持不变。 例如，它可以学习多个过滤器，每个过滤器检测相同模式的不同旋转（例如手写数字；见图 14-10），并且深度最大池化层将确保输出是相同的，无论 回转。 CNN 可以类似地学会对其他任何事物保持不变：厚度、亮度、偏斜、颜色等。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "realistic-field",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl0a46w3ij612l0u0tbt02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "great-groove",
   "metadata": {},
   "source": [
    "Keras 不包含深度最大池化层，但 TensorFlow 的低级深度学习 API 包含：只需使用 tf.nn.max_pool() 函数，并将内核大小和步幅指定为 4 个元组（即大小为 4 的元组） . 每个的前三个值应该是 1：这表示沿着批次、高度和宽度维度的内核大小和步幅应该是 1。最后一个值应该是沿着深度维度你想要的内核大小和步幅——例如 , 3（这必须是输入深度的除数；如果前一层输出 20 个特征图将不起作用，因为 20 不是 3 的倍数）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "advance-emphasis",
   "metadata": {},
   "outputs": [],
   "source": [
    "output = tf.nn.max_pool(images,\n",
    "                        ksize=(1, 1, 1, 3),\n",
    "                        strides=(1, 1, 1, 3),\n",
    "                        padding=\"valid\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "separated-dealer",
   "metadata": {},
   "source": [
    "如果您想将其作为层包含在 Keras 模型中，请将其包装在 Lambda 层中（或创建自定义 Keras 层）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "copyrighted-grammar",
   "metadata": {},
   "outputs": [],
   "source": [
    "depth_pool = keras.layers.Lambda(lambda X: tf.nn.max_pool(X, ksize=(1, 1, 1, 3), \n",
    "                                                          strides=(1, 1, 1, 3),\n",
    "                                                          padding=\"valid\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sized-driver",
   "metadata": {},
   "source": [
    "您在现代架构中经常会看到的最后一种池化层是全局平均池化层。 它的工作方式非常不同：它所做的只是计算每个整个特征图的平均值（它就像一个使用与输入具有相同空间维度的池化内核的平均池化层）。 这意味着它只为每个特征图和每个实例输出一个数字。 虽然这当然是极具破坏性的（特征图中的大部分信息都丢失了），但它可以用作输出层，我们将在本章后面看到。 要创建这样的层，只需使用 keras.layers.GlobalAvgPool2D 类："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "governing-contribution",
   "metadata": {},
   "outputs": [],
   "source": [
    "global_avg_pool = keras.layers.GlobalAvgPool2D()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "going-arrangement",
   "metadata": {},
   "source": [
    "它相当于这个简单的 Lambda 层，它计算空间维度（高度和宽度）的平均值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "massive-mechanics",
   "metadata": {},
   "outputs": [],
   "source": [
    "global_avg_pool = keras.layers.Lambda(lambda X: tf.reduce_mean(X, axis=[1, 2]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "editorial-penny",
   "metadata": {},
   "source": [
    "现在您已经了解了创建卷积神经网络的所有构建块。 让我们看看如何组装它们。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "encouraging-strip",
   "metadata": {},
   "source": [
    "## CNN架构"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "arbitrary-bottom",
   "metadata": {},
   "source": [
    "典型的 CNN 架构堆叠了几个卷积层（每个卷积层后通常跟一个 ReLU 层），然后是一个池化层，然后是另外几个卷积层（+ReLU），然后是另一个池化层，依此类推。 随着图像在网络中前进，图像变得越来越小，但由于卷积层（见图 14-11），它通常也会变得越来越深（即具有更多的特征图）。 在堆栈的顶部，添加了一个常规的前馈神经网络，由几个全连接层（+ReLU）组成，最后一层输出预测（例如，输出估计类概率的 softmax 层）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "occupational-stocks",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl0ngl4qxj61e60g0whe02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "legitimate-heaven",
   "metadata": {},
   "source": [
    "> 一个常见的错误是使用过大的卷积核。 例如，不使用具有 5 × 5 内核的卷积层，而是将具有 3 × 3 内核的两层堆叠起来：它会使用更少的参数和更少的计算，并且通常会表现得更好。 一个例外是第一个卷积层：它通常可以有一个大内核（例如，5 × 5），通常步幅为 2 或更多：这将减少图像的空间维度而不会丢失太多信息，并且由于 输入图像一般只有三个通道，成本不会太高。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "grand-white",
   "metadata": {},
   "source": [
    "以下是如何实现一个简单的 CNN 来处理 Fashion MNIST 数据集（在第 10 章中介绍）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "hungarian-array",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 162 ms (started: 2021-08-18 16:25:11 +08:00)\n"
     ]
    }
   ],
   "source": [
    "from tensorflow import keras\n",
    "\n",
    "model = keras.models.Sequential([\n",
    "    keras.layers.Conv2D(64, 7, activation=\"relu\", padding=\"same\",\n",
    "    input_shape=[28, 28, 1]),\n",
    "    keras.layers.MaxPooling2D(2),\n",
    "    keras.layers.Conv2D(128, 3, activation=\"relu\", padding=\"same\"),\n",
    "    keras.layers.Conv2D(128, 3, activation=\"relu\", padding=\"same\"),\n",
    "    keras.layers.MaxPooling2D(2),\n",
    "    keras.layers.Conv2D(256, 3, activation=\"relu\", padding=\"same\"),\n",
    "    keras.layers.Conv2D(256, 3, activation=\"relu\", padding=\"same\"),\n",
    "    keras.layers.MaxPooling2D(2),\n",
    "    keras.layers.Flatten(),\n",
    "    keras.layers.Dense(128, activation=\"relu\"),\n",
    "    keras.layers.Dropout(0.5),\n",
    "    keras.layers.Dense(64, activation=\"relu\"),\n",
    "    keras.layers.Dropout(0.5),\n",
    "    keras.layers.Dense(10, activation=\"softmax\")\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "liked-wellington",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d (Conv2D)              (None, 28, 28, 64)        3200      \n",
      "_________________________________________________________________\n",
      "max_pooling2d (MaxPooling2D) (None, 14, 14, 64)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_1 (Conv2D)            (None, 14, 14, 128)       73856     \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 14, 14, 128)       147584    \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 7, 7, 128)         0         \n",
      "_________________________________________________________________\n",
      "conv2d_3 (Conv2D)            (None, 7, 7, 256)         295168    \n",
      "_________________________________________________________________\n",
      "conv2d_4 (Conv2D)            (None, 7, 7, 256)         590080    \n",
      "_________________________________________________________________\n",
      "max_pooling2d_2 (MaxPooling2 (None, 3, 3, 256)         0         \n",
      "_________________________________________________________________\n",
      "flatten (Flatten)            (None, 2304)              0         \n",
      "_________________________________________________________________\n",
      "dense (Dense)                (None, 128)               295040    \n",
      "_________________________________________________________________\n",
      "dropout (Dropout)            (None, 128)               0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 64)                8256      \n",
      "_________________________________________________________________\n",
      "dropout_1 (Dropout)          (None, 64)                0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 10)                650       \n",
      "=================================================================\n",
      "Total params: 1,413,834\n",
      "Trainable params: 1,413,834\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "time: 4.99 ms (started: 2021-08-18 16:25:20 +08:00)\n"
     ]
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "inappropriate-accordance",
   "metadata": {},
   "source": [
    "让我们来看看这个模型：\n",
    "* 第一层使用 64 个相当大的过滤器 (7 × 7) 但没有步幅，因为输入图像不是很大。它还设置 input_shape=[28, 28, 1]，因为图像是 28 × 28 像素，具有单个颜色通道（即灰度）。\n",
    "* 接下来我们有一个最大池化层，它使用池大小为 2，因此它将每个空间维度除以因子 2。\n",
    "* 然后我们重复两次相同的结构：两个卷积层，然后是一个最大池化层。对于较大的图像，我们可以多次重复此结构（重复次数是您可以调整的超参数）。\n",
    "* 请注意，当我们将 CNN 向上爬向输出层时，过滤器的数量会增加（最初是 64，然后是 128，然后是 256）：它的增长是有意义的，因为低级特征的数量通常相当低（例如，小圆圈、水平线），但是有许多不同的方法可以将它们组合成更高级别的特征。将每个池化层后的过滤器数量加倍是一种常见的做法：由于池化层将每个空间维度除以因子 2，因此我们可以将下一层中的特征图数量加倍，而不必担心数量会爆炸参数、内存使用或计算负载。\n",
    "* 接下来是全连接网络，由两个隐藏密集层和一个密集输出层组成。请注意，我们必须展平其输入，因为密集网络需要每个实例的一维特征数组。我们还添加了两个 dropout 层，每个 dropout 层的 dropout 率为 50%，以减少过拟合。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "computational-africa",
   "metadata": {},
   "source": [
    "这个 CNN 在测试集上达到了 92% 以上的准确率。它不是最先进的，但它非常好，显然比我们在第 10 章中使用密集网络实现的要好得多。\n",
    "\n",
    "多年来，已经开发了这种基本架构的变体，从而在该领域取得了惊人的进步。这种进步的一个很好的衡量标准是诸如 ILSVRC ImageNet 挑战赛等比赛中的错误率。在这场比赛中，图像分类的前五名错误率在短短六年内从超过 26% 下降到不到 2.3%。前五名错误率是系统的前五名预测不包括正确答案的测试图像的数量。图像很大（256 像素高），有 1,000 个类别，其中一些非常微妙（尝试区分 120 个狗品种）。查看获奖作品的演变是了解 CNN 如何工作的好方法。\n",
    "\n",
    "我们将首先看看经典的 LeNet-5 架构（1998 年），然后是 ILSVRC 挑战赛的三位获胜者：AlexNet（2012 年）、GoogLeNet（2014 年）和 ResNet（2015 年）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "seven-decimal",
   "metadata": {},
   "source": [
    "### LeNet-5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "horizontal-trader",
   "metadata": {},
   "source": [
    "LeNet-5 架构 10 可能是最广为人知的 CNN 架构。 如前所述，它由 Yann LeCun 于 1998 年创建，并已广泛用于手写数字识别（MNIST）。 它由表 14-1 所示的层组成。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "assigned-insert",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl0z5jwr4j613k0fqq4d02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "higher-malta",
   "metadata": {},
   "source": [
    "还有一些额外的细节需要注意：\n",
    "* MNIST 图像为 28 × 28 像素，但在输入网络之前将它们补零至 32 × 32 像素并进行归一化处理。网络的其余部分不使用任何填充，这就是为什么随着图像在网络中前进，尺寸不断缩小。\n",
    "* 平均池化层比平常稍微复杂一些：每个神经元计算其输入的平均值，然后将结果乘以一个可学习系数（每个映射一个）并添加一个可学习的偏差项（同样，每个映射一个），然后最后应用激活函数。\n",
    "* C3 映射中的大多数神经元仅连接到三个或四个 S2 映射（而不是所有六个 S2 映射）中的神经元。有关详细信息，请参阅原始论文 10 中的表 1（第 8 页）。\n",
    "* 输出层有点特殊：不是计算输入和权重向量的矩阵乘法，而是每个神经元输出其输入向量与其权重向量之间的欧几里得距离的平方。每个输出测量图像属于特定数字类别的程度。现在首选交叉熵成本函数，因为它会更多地惩罚错误的预测，产生更大的梯度并更快地收敛。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "focal-shirt",
   "metadata": {},
   "source": [
    "Yann LeCun 的网站提供了 LeNet-5 分类数字的精彩演示。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sound-phoenix",
   "metadata": {},
   "source": [
    "### AlexNet"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "adapted-quality",
   "metadata": {},
   "source": [
    "AlexNet CNN 架构 11 在 2012 年 ImageNet ILSVRC 挑战赛中大获全胜：它实现了 17% 的前五名错误率，而第二名的错误率仅为 26%！ 它由 Alex Krizhevsky（因此得名）、Ilya Sutskever 和 Geoffrey Hinton 开发。 它与 LeNet-5 类似，只是更大更深，并且它是第一个将卷积层直接堆叠在一起，而不是在每个卷积层的顶部堆叠池化层。 表 14-2 展示了这种架构。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "constant-infrastructure",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl13h8iycj614g0lodif02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "applicable-blake",
   "metadata": {},
   "source": [
    "为了减少过拟合，作者使用了两种正则化技术。 首先，他们在训练期间对 F9 和 F10 层的输出应用了 dropout（在第 11 章中介绍），dropout 率为 50%。 其次，他们通过以各种偏移量随机移动训练图像、水平翻转它们并改变光照条件来执行数据增强。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "serial-finish",
   "metadata": {},
   "source": [
    "**数据增强**\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "political-services",
   "metadata": {},
   "source": [
    "数据增强通过生成每个训练实例的许多真实变体来人为地增加训练集的大小。这减少了过度拟合，使其成为一种正则化技术。生成的实例应该尽可能真实：理想情况下，给定来自增强训练集的图像，人类不应该能够判断它是否被增强。简单地添加白噪声无济于事；修改应该是可学习的（白噪声不是）。\n",
    "\n",
    "例如，您可以将训练集中的每张图片略微移动、旋转和调整大小，并将结果图片添加到训练集中（见图 14-12）。这迫使模型更能容忍图片中对象的位置、方向和大小的变化。对于更能容忍不同光照条件的模型，您可以类似地生成许多具有不同对比度的图像。一般情况下，也可以水平翻转图片（文字和其他不对称对象除外）。通过组合这些转换，您可以大大增加训练集的大小。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conventional-boundary",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl1802n0bj61020pg7ay02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "standing-maker",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "tropical-model",
   "metadata": {},
   "source": [
    "AlexNet 还在 C1 和 C3 层的 ReLU 步骤之后立即使用竞争归一化步骤，称为局部响应归一化 (LRN)：最强烈激活的神经元抑制位于相邻特征图中相同位置的其他神经元（这种竞争性激活已被观察到 在生物神经元中）。 这鼓励不同的特征图进行专业化，将它们分开并迫使它们探索更广泛的特征，最终提高泛化能力。 公式 14-2 显示了如何应用 LRN。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "intended-compensation",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl3sq26r5j613608wgm902.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "nasty-velvet",
   "metadata": {},
   "source": [
    "在这个等式中：\n",
    "* $b_i$ 是位于特征图 $i$ 中某行 $u$ 和 $v$ 列的神经元的归一化输出（注意，在此等式中，我们只考虑位于该行和该列的神经元，因此未显示 $u$ 和 $v$）。\n",
    "* $a_i$ 是在 ReLU 步骤之后但在归一化之前激活该神经元。\n",
    "* $k, \\alpha, \\beta $ 和 $r$ 是超参数。 $k$ 称为偏差，$r$ 称为深度半径。\n",
    "* $f_n$ 是特征图的数量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "chief-supervision",
   "metadata": {},
   "source": [
    "例如，如果 r = 2 并且一个神经元具有很强的激活，它将抑制位于其自身上方和下方的特征图中的神经元的激活。\n",
    "\n",
    "在 AlexNet 中，超参数设置如下：r = 2、α = 0.00002、β = 0.75 和 k = 1。可以使用 tf.nn.local_response_normalization() 函数（您可以将其包装在 Lambda 如果你想在 Keras 模型中使用它）。\n",
    "\n",
    "AlexNet 的变体 ZF Net12 由 Matthew Zeiler 和 Rob Fergus 开发，并赢得了 2013 年 ILSVRC 挑战赛。 它本质上是带有一些调整过的超参数（特征图的数量、内核大小、步幅等）的 AlexNet。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "developed-leisure",
   "metadata": {},
   "source": [
    "### GoogLeNet"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "assigned-designation",
   "metadata": {},
   "source": [
    "GoogLeNet 架构由 Christian Szegedy 等人开发。 来自 Google Research，它通过将前五名错误率降低到 7% 以下赢得了 ILSVRC 2014 挑战。 如此出色的性能很大程度上来自于网络比以前的 CNN 更深的事实（如图 14-14 所示）。 这是通过称为初始模块的子网络实现的，它允许 GoogLeNet 比以前的架构更有效地使用参数：GoogLeNet 的参数实际上比 AlexNet 少 10 倍（大约 600 万而不是 6000 万）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fatal-prerequisite",
   "metadata": {},
   "source": [
    "图 14-13 展示了一个 Inception 模块的架构。 符号“3 × 3 + 1(S)”表示该层使用 3 × 3 内核、步长 1 和“相同”填充。 输入信号首先被复制并馈送到四个不同的层。 所有卷积层都使用 ReLU 激活函数。 请注意，第二组卷积层使用不同的内核大小（1 × 1、3 × 3 和 5 × 5），允许它们捕获不同尺度的模式。 另请注意，每一层都使用 1 步长和“相同”填充（甚至是最大池化层），因此它们的输出都具有与输入相同的高度和宽度。 这使得在最终深度连接层中沿深度维度连接所有输出成为可能（即，堆叠来自所有四个顶部卷积层的特征图）。 这个连接层可以在 TensorFlow 中使用 tf.concat() 操作实现，axis=3（轴是深度）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "handled-setup",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl42k5058j61cs0nmjuv02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dental-joshua",
   "metadata": {},
   "source": [
    "您可能想知道为什么 Inception 模块具有 1 × 1 内核的卷积层。 这些层肯定不能捕捉任何特征，因为它们一次只看一个像素？ 事实上，这些层有三个目的：\n",
    "* 虽然它们不能捕捉空间模式，但它们可以捕捉沿深度维度的模式。\n",
    "* 它们被配置为输出比输入更少的特征图，因此它们充当瓶颈层，这意味着它们降低了维度。 这降低了计算成本和参数数量，加快了训练速度并提高了泛化能力。\n",
    "* 每对卷积层（[1 × 1, 3 × 3] 和 [1 × 1, 5 × 5]）就像一个强大的卷积层，能够捕捉更复杂的模式。 事实上，这对卷积层不是在图像上扫描一个简单的线性分类器（就像单个卷积层那样），而是在图像上扫描一个两层神经网络。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fundamental-funds",
   "metadata": {},
   "source": [
    "简而言之，您可以将整个 inception 模块视为类固醇上的卷积层，能够输出捕获各种尺度复杂模式的特征图。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "spatial-treaty",
   "metadata": {},
   "source": [
    "> 每个卷积层的卷积核数是一个超参数。 不幸的是，这意味着您需要为添加的每个初始层调整 6 个超参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "nuclear-moore",
   "metadata": {},
   "source": [
    "现在让我们看看 GoogLeNet CNN 的架构（见图 14-14）。 每个卷积层和每个池化层输出的特征图数量显示在内核大小之前。 该架构非常深，必须用三列表示，但 GoogLeNet 实际上是一个高堆栈，包括九个初始模块（带有陀螺的盒子）。 inception 模块中的六个数字代表模块中每个卷积层输出的特征图数量（顺序与图 14-13 相同）。 请注意，所有卷积层都使用 ReLU 激活函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sublime-westminster",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl47fud6tj60ym0r2jwn02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "external-eclipse",
   "metadata": {},
   "source": [
    "让我们通过这个网络：\n",
    "* 前两层将图像的高度和宽度除以 4（因此其面积除以 16），以减少计算负载。第一层使用大内核大小，以便保留大部分信息。\n",
    "* 然后局部响应归一化层确保前面的层学习各种各样的特征（如前所述）。\n",
    "* 后面有两个卷积层，第一个像瓶颈层。如前所述，您可以将这一对视为单个更智能的卷积层。\n",
    "* 同样，局部响应归一化层可确保前面的层捕获多种模式。\n",
    "* 接下来，最大池化层将图像高度和宽度减少 2，再次加快计算速度。\n",
    "* 然后是九个初始模块的高堆栈，与几个最大池化层交错以降低维度并加速网络。\n",
    "* 接下来，全局平均池化层输出每个特征图的均值：这会丢弃任何剩余的空间信息，这很好，因为那时没有太多空间信息。实际上，GoogLeNet 输入图像通常预期为 224 × 224 像素，因此在 5 个最大池化层之后，每个层将高度和宽度除以 2，特征图下降到 7 × 7。此外，这是一个分类任务，而不是本地化，所以对象在哪里并不重要。由于这一层带来的降维，在 CNN 的顶部不需要有几个全连接层（就像在 AlexNet 中那样），这大大减少了网络中的参数数量并限制了过拟合的风险。\n",
    "* 最后一层是不言自明的：用于正则化的 dropout，然后是一个具有 1,000 个单元的全连接层（因为有 1,000 个类）和一个 softmax 激活函数来输出估计的类概率。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "boolean-expansion",
   "metadata": {},
   "source": [
    "该图略有简化：原始的 GoogLeNet 架构还包括插入在第三个和第六个初始模块顶部的两个辅助分类器。 它们都由一个平均池化层、一个卷积层、两个全连接层和一个 softmax 激活层组成。 在训练期间，他们的损失（按比例缩小 70%）被添加到整体损失中。 目标是解决梯度消失问题并规范网络。\n",
    "\n",
    "然而，后来证明它们的影响相对较小。 谷歌研究人员后来提出了 GoogLeNet 架构的几种变体，包括 Inception-v3 和 Inception-v4，使用稍微不同的 Inception 模块并达到更好的性能。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "liberal-stamp",
   "metadata": {},
   "source": [
    "### VGGNet"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fresh-aquarium",
   "metadata": {},
   "source": [
    "ILSVRC 2014 挑战赛的亚军是 VGGNet，它由牛津大学视觉几何组 (VGG) 研究实验室的 Karen Simonyan 和 Andrew Zisserman 开发。 它有一个非常简单和经典的架构，有 2 或 3 个卷积层和一个池化层，然后又是 2 或 3 个卷积层和一个池化层，依此类推（总共只有 16 或 19 个卷积层，具体取决于 VGG 变体），加上具有 2 个隐藏层和输出层的最终密集网络。 它只使用了 3 × 3 个过滤器，但使用了许多过滤器。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "liquid-lotus",
   "metadata": {},
   "source": [
    "### ResNet"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "imported-sailing",
   "metadata": {},
   "source": [
    "何开明等。 使用残差网络（或 ResNet）赢得了 ILSVRC 2015 挑战，其前五名错误率低于 3.6%，令人震惊。 获胜的变体使用了由 152 层组成的极深 CNN（其他变体有 34、50 和 101 层）。 它印证了大趋势：模型越来越深，参数越来越少。 能够训练这样一个深度网络的关键是使用跳过连接（也称为快捷连接）：输入层的信号也被添加到位于堆栈更高一点的层的输出中。 让我们看看为什么这很有用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "renewable-casting",
   "metadata": {},
   "source": [
    "训练神经网络时，目标是使其建模目标函数 h(x)。 如果将输入 x 添加到网络的输出（即添加跳过连接），则网络将被迫建模 f(x) = h(x) – x 而不是 h(x)。 这称为残差学习（见图 14-15）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "prompt-donna",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl4fhclb9j61e40mo40b02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fundamental-singing",
   "metadata": {},
   "source": [
    "初始化常规神经网络时，其权重接近于零，因此网络仅输出接近于零的值。 如果添加跳过连接，生成的网络只会输出其输入的副本； 换句话说，它最初对恒等函数进行建模。 如果目标函数非常接近恒等函数（通常是这种情况），这将大大加快训练速度。\n",
    "\n",
    "此外，如果添加许多跳过连接，即使有几个层尚未开始学习，网络也可以开始取得进展（见图 14-16）。 由于跳过连接，信号可以轻松地穿过整个网络。 深度残差网络可以看作是一堆残差单元（RU），其中每个残差单元都是一个带有跳跃连接的小型神经网络。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "stuffed-venue",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl4hp8cx3j60yy0n876c02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "copyrighted-picnic",
   "metadata": {},
   "source": [
    "现在让我们看看 ResNet 的架构（见图 14-17）。 这是令人惊讶的简单。 它的开始和结束与 GoogLeNet 完全一样（除了没有 dropout 层），中间只是一堆非常深的简单残差单元。 每个残差单元由两个卷积层组成（没有池化层！），使用 3 × 3 内核和保留空间维度（步长 1，“相同”填充），使用批量归一化 (BN) 和 ReLU 激活。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "leading-numbers",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl4iwgux8j618a0s0gqu02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "nasty-coaching",
   "metadata": {},
   "source": [
    "请注意，特征图的数量每几个残差单元加倍，同时它们的高度和宽度减半（使用步幅为 2 的卷积层）。 发生这种情况时，输入不能直接添加到残差单元的输出中，因为它们的形状不同（例如，这个问题影响了图 14-17 中虚线箭头表示的跳过连接）。 为了解决这个问题，输入通过一个 1 × 1 的卷积层，步长为 2，输出特征图的数量正确（见图 14-18）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "compatible-transcript",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl4jsjzjaj61800igtas02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "provincial-gabriel",
   "metadata": {},
   "source": [
    "ResNet-34 是具有 34 层（仅计算卷积层和全连接层）17 的 ResNet，包含 3 个残差单元，输出 64 个特征图、128 个图的 RU、256 个图的 6 个 RU 和 512 个图的 3 个 RU。 我们将在本章后面实现这个架构。\n",
    "\n",
    "比这更深的 ResNet，例如 ResNet-152，使用略有不同的残差单元。 它们不是两个具有 256 个特征图的 3 × 3 卷积层，而是使用三个卷积层：首先是一个只有 64 个特征图（少 4 倍）的 1 × 1 卷积层，它充当瓶颈层（如前所述） )，然后是具有 64 个特征图的 3 × 3 层，最后是具有 256 个特征图（4 乘以 64）的另一个 1 × 1 卷积层，用于恢复原始深度。 ResNet-152 包含 3 个这样的 RU，输出 256 个地图，然后是 8 个具有 512 个地图的 RU，高达 36 个具有 1,024 个地图的 RU，最后是 3 个具有 2,048 个地图的 RU。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dominican-fusion",
   "metadata": {},
   "source": [
    "> Google 的 Inception-v4 架构融合了 GoogLe-Net 和 ResNet 的思想，在 ImageNet 分类上实现了接近 3% 的前五错误率。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "figured-heading",
   "metadata": {},
   "source": [
    "### Xception"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "meaning-bhutan",
   "metadata": {},
   "source": [
    "GoogLeNet 架构的另一个变体值得注意：Xception19（代表 Extreme Inception）由 François Chollet（Keras 的作者）在 2016 年提出，它在一个巨大的视觉任务（3.5 亿张图像和 17,000类）。就像 Inception-v4 一样，它融合了 GoogLeNet 和 ResNet 的思想，但它用一种称为深度可分离卷积层（或简称可分离卷积层）的特殊类型层代替了 inception 模块。这些层以前曾在一些 CNN 架构中使用过，但它们不像 Xception 架构那样处于中心位置。虽然常规卷积层使用过滤器尝试同时捕获空间模式（例如，椭圆形）和跨通道模式（例如，嘴 + 鼻子 + 眼睛 = 脸），但可分离的卷积层强烈假设空间模式和交叉- 通道模式可以单独建模（见图 14-19）。因此，它由两部分组成：第一部分为每个输入特征图应用单个空间过滤器，然后第二部分专门用于跨通道模式——它只是一个具有 1 × 1 过滤器的常规卷积层。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "modular-database",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl4o5ylgej618i0tetcw02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "signed-technician",
   "metadata": {},
   "source": [
    "由于可分离卷积层的每个输入通道只有一个空间过滤器，因此您应该避免在通道太少的层之后使用它们，例如输入层（当然，这就是图 14-19 所表示的，但这只是为了说明目的） . 出于这个原因，Xception 架构从 2 个常规卷积层开始，但架构的其余部分仅使用可分离卷积（总共 34 个），加上一些最大池化层和通常的最终层（一个全局平均池化层和一个 密集输出层）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "residential-pharmacy",
   "metadata": {},
   "source": [
    "您可能想知道为什么 Xception 被认为是 GoogLeNet 的一个变体，因为它根本不包含 Inception 模块。 好吧，正如我们之前所讨论的，初始模块包含带有 1 × 1 过滤器的卷积层：这些过滤器专门用于跨通道模式。 然而，位于它们之上的卷积层是常规卷积层，它们寻找空间和跨通道模式。 因此，您可以将初始模块视为常规卷积层（联合考虑空间模式和跨通道模式）和可分离卷积层（分别考虑它们）之间的中间体。 在实践中，似乎可分离的卷积层通常表现更好。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "comparable-arrest",
   "metadata": {},
   "source": [
    "> 可分离的卷积层比普通卷积层使用更少的参数、更少的内存和更少的计算，并且通常它们甚至表现得更好，所以你应该考虑默认使用它们（除了很少通道的层之后）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "annoying-entry",
   "metadata": {},
   "source": [
    "ILSVRC 2016 挑战赛由香港中文大学的 CUImage 团队赢得。 他们使用了许多不同技术的集合，包括称为 GBD-Net 的复杂对象检测系统，以实现低于 3% 的前五名错误率。 尽管这个结果无疑令人印象深刻，但解决方案的复杂性与 ResNets 的简单性形成了对比。 此外，一年后，另一个相当简单的架构表现得更好，正如我们现在将看到的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "biblical-samoa",
   "metadata": {},
   "source": [
    "### SENet"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "accessible-chapter",
   "metadata": {},
   "source": [
    "ILSVRC 2017 挑战赛的获胜架构是 Squeeze-and Excitation Network (SENet)。 这种架构扩展了现有架构，例如初始网络和 ResNets，并提高了它们的性能。 这让 SENet 以惊人的 2.25% 前五错误率赢得了比赛！ Inception 网络和 ResNet 的扩展版本分别称为 SE-Inception 和 SE-ResNet。 提升来自这样一个事实，即 SENet 向原始架构中的每个单元（即每个初始模块或每个残差单元）添加了一个称为 SE 块的小型神经网络，如图 14-20 所示。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "wicked-channels",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl4wapa02j61800kitae02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "corrected-killer",
   "metadata": {},
   "source": [
    "SE 块分析它所连接的单元的输出，只关注深度维度（它不寻找任何空间模式），并了解哪些特征通常最活跃。 然后使用此信息重新校准特征图，如图 14-21 所示。 例如，SE 块可以了解到嘴巴、鼻子和眼睛通常在图片中一起出现：如果您看到嘴巴和鼻子，您应该也看到眼睛。 所以如果块在嘴巴和鼻子特征图中看到强烈的激活，但在眼睛特征图中只有轻微激活，它会提升眼睛特征图（更准确地说，它会减少不相关的特征图）。 如果眼睛与其他东西有些混淆，则此特征图重新校准将有助于解决歧义。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "driven-cycle",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl4z5uk1jj618i0isgnw02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "working-headline",
   "metadata": {},
   "source": [
    "SE 块仅由三层组成：全局平均池化层、使用 ReLU 激活函数的隐藏密集层和使用 sigmoid 激活函数的密集输出层（见图 14-22）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dental-youth",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl50z95m1j617m0iyabi02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "structured-welsh",
   "metadata": {},
   "source": [
    "如前所述，全局平均池化层计算每个特征图的平均激活：例如，如果它的输入包含 256 个特征图，它将输出 256 个数字，代表每个过滤器的整体响应水平。下一层是“挤压”发生的地方：这一层的神经元明显少于 256 个——通常比特征图的数量（例如，16 个神经元）少 16 倍，因此 256 个数字被压缩成一个小向量（例如，16方面）。这是特征响应分布的低维向量表示（即嵌入）。这个瓶颈步骤迫使 SE 块学习特征组合的一般表示（当我们在第 17 章讨论自动编码器时，我们将再次看到这个原理的作用）。最后，输出层采用嵌入并输出一个重新校准向量，每个特征图包含一个数字（例如，256），每个数字在 0 和 1 之间。然后将特征图乘以这个重新校准向量，因此不相关的特征（低重新校准分数）被缩小，而相关特征（重新校准分数接近 1）被保留。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eastern-start",
   "metadata": {},
   "source": [
    "## 使用 Keras 实现 ResNet-34 CNN"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "central-blink",
   "metadata": {},
   "source": [
    "到目前为止描述的大多数 CNN 架构都相当容易实现（尽管通常你会加载一个预训练的网络，正如我们将看到的）。 为了说明这个过程，让我们使用 Keras 从头开始实现一个 ResNet-34。 首先，让我们创建一个 ResidualUnit 层："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "critical-jesus",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 842 µs (started: 2021-08-18 21:16:10 +08:00)\n"
     ]
    }
   ],
   "source": [
    "from tensorflow import keras\n",
    "from tensorflow.keras.utils import plot_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "internal-disclosure",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 1.88 ms (started: 2021-08-18 21:16:14 +08:00)\n"
     ]
    }
   ],
   "source": [
    "class ResidualUnit(keras.layers.Layer):\n",
    "    def __init__(self, filters, strides=1, activation=\"relu\", **kwargs):\n",
    "        super().__init__(**kwargs)\n",
    "        self.activation = keras.activations.get(activation)\n",
    "        self.main_layers = [\n",
    "            keras.layers.Conv2D(filters, 3, \n",
    "                                strides=strides,\n",
    "                                padding=\"same\", \n",
    "                                use_bias=False),\n",
    "            keras.layers.BatchNormalization(),\n",
    "            self.activation,\n",
    "            keras.layers.Conv2D(filters, 3, \n",
    "                                strides=1, \n",
    "                                padding=\"same\", \n",
    "                                use_bias=False),\n",
    "            keras.layers.BatchNormalization()]\n",
    "        self.skip_layers = []\n",
    "        if strides > 1:\n",
    "            self.skip_layers = [keras.layers.Conv2D(filters, 1, \n",
    "                                                    strides=strides,\n",
    "                                                    padding=\"same\", \n",
    "                                                    use_bias=False),\n",
    "                                keras.layers.BatchNormalization()]\n",
    "        \n",
    "    def call(self, inputs):\n",
    "        Z = inputs\n",
    "        for layer in self.main_layers: \n",
    "            Z = layer(Z)\n",
    "        skip_Z = inputs\n",
    "        \n",
    "        for layer in self.skip_layers:\n",
    "            skip_Z = layer(skip_Z)\n",
    "        return self.activation(Z + skip_Z)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "motivated-infrared",
   "metadata": {},
   "source": [
    "如您所见，此代码与图 14-18 非常匹配。 在构造函数中，我们创建了所有需要的层：主层是图右侧的层，而跳过层是左侧的层（仅当步幅大于 1 时才需要）。 然后在 call() 方法中，我们让输入通过主层和跳过层（如果有），然后我们添加两个输出并应用激活函数。\n",
    "\n",
    "接下来，我们可以使用 Sequential 模型构建 ResNet-34，因为它实际上只是一个很长的层序列（现在我们有了 ResidualUnit 类，我们可以将每个残差单元视为单个层）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "serious-magnet",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 8.34 s (started: 2021-08-18 21:18:15 +08:00)\n"
     ]
    }
   ],
   "source": [
    "model = keras.models.Sequential()\n",
    "\n",
    "model.add(keras.layers.Conv2D(64, 7, strides=2, input_shape=[224, 224, 3],\n",
    "                              padding=\"same\", use_bias=False))\n",
    "model.add(keras.layers.BatchNormalization())\n",
    "model.add(keras.layers.Activation(\"relu\"))\n",
    "model.add(keras.layers.MaxPool2D(pool_size=3, strides=2, padding=\"same\"))\n",
    "\n",
    "prev_filters = 64\n",
    "for filters in [64] * 3 + [128] * 4 + [256] * 6 + [512] * 3:\n",
    "    strides = 1 if filters == prev_filters else 2\n",
    "    model.add(ResidualUnit(filters, strides=strides))\n",
    "    prev_filters = filters\n",
    "    \n",
    "model.add(keras.layers.GlobalAvgPool2D())\n",
    "model.add(keras.layers.Flatten())\n",
    "model.add(keras.layers.Dense(10, activation=\"softmax\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "starting-selection",
   "metadata": {},
   "source": [
    "这段代码中唯一稍微有点棘手的部分是将 ResidualUnit 层添加到模型的循环：如前所述，前 3 个 RU 有 64 个过滤器，然后接下来的 4 个 RU 有 128 个过滤器，依此类推。 然后，当过滤器的数量与前一个 RU 中的相同时，我们将步幅设置为 1，否则我们将其设置为 2。然后我们添加 ResidualUnit，最后我们更新 prev_filters。\n",
    "\n",
    "令人惊讶的是，在不到 40 行代码中，我们就可以构建赢得 ILSVRC 2015 挑战的模型！ 这展示了 ResNet 模型的优雅和 Keras API 的表现力。 实现其他 CNN 架构并不难。 然而，Keras 内置了几个这样的架构，那么为什么不改用它们呢？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "stone-finance",
   "metadata": {},
   "source": [
    "## 使用来自 Keras 的预训练模型"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "substantial-headline",
   "metadata": {},
   "source": [
    "通常，您不必手动实现 GoogLeNet 或 ResNet 等标准模型，因为预训练网络可以通过 keras.applications 包中的一行代码轻松获得。 例如，您可以使用以下代码行加载在 ImageNet 上预训练的 ResNet-50 模型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "alike-lawyer",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 5.29 s (started: 2021-08-18 21:20:22 +08:00)\n"
     ]
    }
   ],
   "source": [
    "model = keras.applications.resnet50.ResNet50(weights=\"imagenet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "initial-timeline",
   "metadata": {},
   "source": [
    "就这样！ 这将创建一个 ResNet-50 模型并下载在 ImageNet 数据集上预训练的权重。 要使用它，您首先需要确保图像具有正确的大小。ResNet-50 模型需要 224 × 224 像素的图像（其他模型可能需要其他尺寸，例如 299 × 299），所以让我们使用 TensorFlow 的 tf.image.resize() 函数来调整我们之前加载的图像的大小："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tired-bhutan",
   "metadata": {},
   "outputs": [],
   "source": [
    "images_resized = tf.image.resize(images, [224, 224])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "hollywood-stephen",
   "metadata": {},
   "source": [
    "tf.image.resize() 不会保留纵横比。 如果这是一个问题，请在调整大小之前尝试将图像裁剪为适当的纵横比。 两种操作都可以使用 tf.image.crop_and_resize() 一次性完成。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "confident-drama",
   "metadata": {},
   "source": [
    "预训练模型假设图像以特定方式进行了预处理。 在某些情况下，他们可能希望输入从 0 缩放到 1，或从 –1 缩放到 1，依此类推。 每个模型都提供了一个 preprocess_input() 函数，您可以使用它来预处理您的图像。 这些函数假设像素值的范围为 0 到 255，因此我们必须将它们乘以 255（因为之前我们将它们缩放到 0-1 范围）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "realistic-nation",
   "metadata": {},
   "outputs": [],
   "source": [
    "inputs = keras.applications.resnet50.preprocess_input(images_resized * 255)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bottom-murray",
   "metadata": {},
   "source": [
    "现在我们可以使用预训练模型进行预测："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "independent-myanmar",
   "metadata": {},
   "outputs": [],
   "source": [
    "Y_proba = model.predict(inputs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "attempted-theme",
   "metadata": {},
   "source": [
    "像往常一样，输出 Y_proba 是一个矩阵，每个图像一行，每个类别一列（在这种情况下，有 1,000 个类别）。 如果要显示前 K 个预测，包括类名和每个预测类的估计概率，请使用 decode_predictions() 函数。 对于每张图像，它返回一个包含前 K 个预测的数组，其中每个预测表示为一个包含类标识符、23 其名称和相应置信度分数的数组："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bigger-soldier",
   "metadata": {},
   "outputs": [],
   "source": [
    "top_K = keras.applications.resnet50.decode_predictions(Y_proba, top=3)\n",
    "for image_index in range(len(images)):\n",
    "    print(\"Image #{}\".format(image_index))\n",
    "    for class_id, name, y_proba in top_K[image_index]:\n",
    "        print(\" {} - {:12s} {:.2f}%\".format(class_id, name, y_proba * 100))\n",
    "        print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "chubby-reservation",
   "metadata": {},
   "source": [
    "输出如下所示："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "imposed-shock",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtl9mt0zpoj61aa076dgc02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "amino-composite",
   "metadata": {},
   "source": [
    "正确的类（修道院和雏菊）出现在两个图像的前三个结果中。 考虑到模型必须从 1,000 个类中进行选择，这很好。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "separate-diary",
   "metadata": {},
   "source": [
    "如您所见，使用预训练模型创建一个非常好的图像分类器非常容易。 keras.applications 中提供了其他视觉模型，包括几个 ResNet 变体、GoogLeNet 变体（如 Inception-v3 和 Xception）、VGGNet 变体以及 MobileNet 和 MobileNetV2（用于移动应用程序的轻量级模型）。\n",
    "\n",
    "但是，如果您想对不属于 ImageNet 的图像类别使用图像分类器怎么办？ 在这种情况下，您仍然可以从预训练模型中受益以执行迁移学习。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "palestinian-bloom",
   "metadata": {},
   "source": [
    "## 用于迁移学习的预训练模型"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "artificial-colon",
   "metadata": {},
   "source": [
    "如果你想建立一个图像分类器但你没有足够的训练数据，那么重用一个预训练模型的较低层通常是个好主意，正如我们在第 11 章中讨论的那样。 例如，让我们训练一个模型来分类 鲜花图片，重用预训练的 Xception 模型。 首先，让我们使用 TensorFlow Datasets 加载数据集（参见第 13 章）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "detected-beverage",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1mDownloading and preparing dataset Unknown size (download: Unknown size, generated: Unknown size, total: Unknown size) to /home/huangwei/tensorflow_datasets/tf_flowers/3.0.1...\u001b[0m\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "fc23ba0e792c44b28dd0ed0c6ce75ce4",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Dl Completed...: 0 url [00:00, ? url/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1e2ec1095ace4403a9110526851c2762",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Dl Size...: 0 MiB [00:00, ? MiB/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "870f36e5679c4ab7ac295ff15f1db6cb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Generating splits...:   0%|          | 0/1 [00:00<?, ? splits/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "16b7f64c3c994df9bf8b58e90ca752d4",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Generating train examples...: 0 examples [00:00, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8c82846da1ba4a6ca1806f73373818b0",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Shuffling tf_flowers-train.tfrecord...:   0%|          | 0/3670 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1mDataset tf_flowers downloaded and prepared to /home/huangwei/tensorflow_datasets/tf_flowers/3.0.1. Subsequent calls will reuse this data.\u001b[0m\n",
      "time: 5min 37s (started: 2021-08-18 21:36:34 +08:00)\n"
     ]
    }
   ],
   "source": [
    "import tensorflow_datasets as tfds\n",
    "\n",
    "dataset, info = tfds.load(\"tf_flowers\", as_supervised=True, with_info=True)\n",
    "\n",
    "dataset_size = info.splits[\"train\"].num_examples # 3670\n",
    "class_names = info.features[\"label\"].names # [\"dandelion\", \"daisy\", ...]\n",
    "n_classes = info.features[\"label\"].num_classes # 5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "crude-luxury",
   "metadata": {},
   "source": [
    "请注意，您可以通过设置 with_info=True 来获取有关数据集的信息。 在这里，我们获得了数据集大小和类的名称。 不幸的是，只有一个“训练”数据集，没有测试集或验证集，所以我们需要拆分训练集。 TF Datasets 项目为此提供了一个 API。 例如，让我们将数据集的前 10% 用于测试，接下来的 15% 用于验证，剩余的 75% 用于训练："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "historic-groove",
   "metadata": {},
   "outputs": [],
   "source": [
    "test_split, valid_split, train_split = tfds.Split.TRAIN.subsplit=[10, 15, 75]\n",
    "\n",
    "test_set = tfds.load(\"tf_flowers\", split=test_split, as_supervised=True)\n",
    "valid_set = tfds.load(\"tf_flowers\", split=valid_split, as_supervised=True)\n",
    "train_set = tfds.load(\"tf_flowers\", split=train_split, as_supervised=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "respective-province",
   "metadata": {},
   "source": [
    "接下来我们必须预处理图像。 CNN 需要 224 × 224 的图像，因此我们需要调整它们的大小。 我们还需要通过 Xception 的 preprocess_input() 函数运行图像："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "fleet-modem",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 828 µs (started: 2021-08-18 22:05:20 +08:00)\n"
     ]
    }
   ],
   "source": [
    "def preprocess(image, label):\n",
    "    resized_image = tf.image.resize(image, [224, 224])\n",
    "    final_image = keras.applications.xception.preprocess_input(resized_image)\n",
    "    return final_image, label"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "exact-thousand",
   "metadata": {},
   "source": [
    "让我们将此预处理函数应用于所有三个数据集，对训练集进行混洗，并为所有数据集添加批处理和预取："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "removed-daily",
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 32\n",
    "\n",
    "train_set = train_set.shuffle(1000)\n",
    "\n",
    "train_set = train_set.map(preprocess).batch(batch_size).prefetch(1)\n",
    "valid_set = valid_set.map(preprocess).batch(batch_size).prefetch(1)\n",
    "test_set = test_set.map(preprocess).batch(batch_size).prefetch(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "personal-steal",
   "metadata": {},
   "source": [
    "如果要执行一些数据增强，请更改训练集的预处理函数，向训练图像添加一些随机变换。 例如，使用 tf.image.random_crop() 随机裁剪图像，使用 tf.image.random_flip_left_right() 随机水平翻转图像，等等（请参阅笔记本的“迁移学习的预训练模型”部分以了解 一个例子）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "grateful-johns",
   "metadata": {},
   "source": [
    "> keras.preprocessing.image.ImageDataGenerator 类可以轻松地从磁盘加载图像并以各种方式扩充它们：您可以移动每个图像、旋转它、重新缩放它、水平或垂直翻转它、剪切它或应用任何转换函数 你想要它。 这对于简单的项目来说非常方便。 然而，构建 tf.data 管道有许多优点：它可以从任何来源高效（例如并行）读取图像，而不仅仅是本地磁盘； 您可以随意操作数据集； 如果你编写了一个基于 tf.image 操作的预处理函数，这个函数可以在 tf.data 管道和你将部署到生产的模型中使用（参见第 19 章）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "national-snake",
   "metadata": {},
   "source": [
    "接下来让我们加载在 ImageNet 上预训练的 Xception 模型。 我们通过设置 include_top=False 排除网络的顶部：这排除了全局平均池化层和密集输出层。 然后我们根据基础模型的输出添加我们自己的全局平均池化层，然后是一个密集输出层，每个类一个单元，使用 softmax 激活函数。 最后，我们创建 Keras 模型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "caroline-machine",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 5.31 s (started: 2021-08-18 22:05:07 +08:00)\n"
     ]
    }
   ],
   "source": [
    "base_model = keras.applications.xception.Xception(weights=\"imagenet\",\n",
    "                                                  include_top=False)\n",
    "avg = keras.layers.GlobalAveragePooling2D()(base_model.output)\n",
    "output = keras.layers.Dense(n_classes, activation=\"softmax\")(avg)\n",
    "model = keras.Model(inputs=base_model.input, outputs=output)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "threaded-genetics",
   "metadata": {},
   "source": [
    "如第 11 章所述，冻结预训练层的权重通常是个好主意，至少在训练开始时："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "lyric-twins",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time: 4.86 ms (started: 2021-08-18 22:11:08 +08:00)\n"
     ]
    }
   ],
   "source": [
    "for layer in base_model.layers:\n",
    "    layer.trainable = False"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "blind-trace",
   "metadata": {},
   "source": [
    "> 由于我们的模型直接使用基础模型的层，而不是 base_model 对象本身，因此设置 base_model.trainable=False 将不起作用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "juvenile-olympus",
   "metadata": {},
   "source": [
    "最后，我们可以编译模型并开始训练："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "finite-oxford",
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = keras.optimizers.SGD(lr=0.2, momentum=0.9, decay=0.01)\n",
    "model.compile(loss=\"sparse_categorical_crossentropy\", \n",
    "              optimizer=optimizer,\n",
    "              metrics=[\"accuracy\"])\n",
    "history = model.fit(train_set, epochs=5, validation_data=valid_set)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "capable-layout",
   "metadata": {},
   "source": [
    "这将非常缓慢，除非您有 GPU。 如果你不这样做，那么你应该在 Colab 中运行本章的笔记本，使用 GPU 运行时（它是免费的！）。 请参阅 https://github.com/ageron/handson-ml2 上的说明。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "tracked-rental",
   "metadata": {},
   "source": [
    "在对模型进行几个 epoch 训练后，其验证准确率应达到 75-80% 左右，并且不再取得太大进展。 这意味着顶层现在已经训练得很好，所以我们准备解冻所有层（或者你可以尝试只解冻顶层）并继续训练（不要忘记在你冻结或解冻层时编译模型 ）。 这次我们使用低得多的学习率来避免损坏预训练的权重："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "beautiful-palestine",
   "metadata": {},
   "outputs": [],
   "source": [
    "for layer in base_model.layers:\n",
    "    layer.trainable = True\n",
    "    \n",
    "optimizer = keras.optimizers.SGD(lr=0.01, momentum=0.9, decay=0.001)\n",
    "model.compile(...)\n",
    "history = model.fit(...)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sustained-stream",
   "metadata": {},
   "source": [
    "这需要一段时间，但这个模型在测试集上应该达到 95% 左右的准确率。 有了它，你就可以开始训练惊人的图像分类器了！ 但计算机视觉不仅仅是分类。 例如，如果您还想知道花在图片中的位置怎么办？ 现在让我们看看这个。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "immune-rally",
   "metadata": {},
   "source": [
    "## 分类和定位"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dynamic-chase",
   "metadata": {},
   "source": [
    "定位图片中的对象可以表示为回归任务，如第 10 章所述：要预测对象周围的边界框，常用的方法是预测对象中心的水平和垂直坐标，以及其高度 和宽度。 这意味着我们有四个数字要预测。 它不需要对模型进行太多更改； 我们只需要添加具有四个单元的第二个密集输出层（通常在全局平均池化层的顶部），并且可以使用 MSE 损失进行训练："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "postal-impossible",
   "metadata": {},
   "outputs": [],
   "source": [
    "base_model = keras.applications.xception.Xception(weights=\"imagenet\",\n",
    "                                                  include_top=False)\n",
    "\n",
    "avg = keras.layers.GlobalAveragePooling2D()(base_model.output)\n",
    "class_output = keras.layers.Dense(n_classes, activation=\"softmax\")(avg)\n",
    "loc_output = keras.layers.Dense(4)(avg)\n",
    "model = keras.Model(inputs=base_model.input,\n",
    "                    outputs=[class_output, \n",
    "                             loc_output])\n",
    "model.compile(loss=[\"sparse_categorical_crossentropy\", \"mse\"],\n",
    "              loss_weights=[0.8, 0.2], # depends on what you care most about\n",
    "              optimizer=optimizer, metrics=[\"accuracy\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "current-consideration",
   "metadata": {},
   "source": [
    "但是现在我们遇到了一个问题：鲜花数据集没有围绕鲜花的边界框。所以，我们需要自己添加它们。这通常是机器学习项目中最困难和最昂贵的部分之一：获取标签。花时间寻找合适的工具是个好主意。要使用边界框注释图像，您可能需要使用开源图像标记工具，例如 VGG Image Annotator、LabelImg、OpenLabeler 或 ImgLab，或者可能是商业工具，例如 LabelBox 或 Supervisely。如果您有大量要注释的图像，您可能还需要考虑众包平台，例如 Amazon Mechanical Turk。但是，搭建众包平台，准备表格发给工人，监督他们，确保他们生产的边界框的质量是好的，所以要确保它是值得的。努力。如果要标记的图像只有几千张，并且您不打算经常这样做，那么最好自己做。阿德里亚娜·科瓦什卡等人。写了一篇关于计算机视觉众包的非常实用的论文。我建议你检查一下，即使你不打算使用众包。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "interesting-migration",
   "metadata": {},
   "source": [
    "假设您已经获得了花数据集中每个图像的边界框（现在我们假设每个图像有一个边界框）。 然后，您需要创建一个数据集，其项目将是一批经过预处理的图像及其类标签和边界框。 每个项目都应该是形式（图像，（class_labels，bounding_boxes））的元组。 然后你就可以训练你的模型了！"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sapphire-impression",
   "metadata": {},
   "source": [
    "边界框应该标准化，以便水平和垂直坐标以及高度和宽度都在 0 到 1 的范围内。此外，通常预测高度和宽度的平方根而不是高度和宽度 直接：这样，大边界框的 10 像素错误不会像小边界框的 10 像素错误那样受到惩罚。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "backed-sunrise",
   "metadata": {},
   "source": [
    "MSE 通常可以很好地作为训练模型的成本函数，但它不是评估模型预测边界框的好坏指标。 最常见的度量是联合交集 (IoU)：预测边界框和目标边界框之间的重叠区域除以它们的联合区域（见图 14-23）。 在 tf.keras 中，它由 tf.keras.metrics.MeanIoU 类实现。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "quality-measure",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtlb2osm3rj61eg0sqtgm02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "genuine-league",
   "metadata": {},
   "source": [
    "对单个对象进行分类和定位很不错，但是如果图像包含多个对象怎么办（就像在花数据集中经常发生的情况一样）？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "accessible-rebound",
   "metadata": {},
   "source": [
    "## 物体检测"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "racial-practitioner",
   "metadata": {},
   "source": [
    "对图像中的多个对象进行分类和定位的任务称为对象检测。直到几年前，一种常见的方法是采用经过训练的 CNN 对单个对象进行分类和定位，然后将其滑过图像，如图 14-24 所示。在这个例子中，图像被切割成一个 6 × 8 的网格，我们展示了一个 CNN（黑色粗矩形）在所有 3 × 3 区域上滑动。当 CNN 查看图像的左上角时，它检测到最左侧玫瑰的一部分，然后当它第一次向右移动一步时再次检测到同一朵玫瑰。下一步，它开始检测最上面的玫瑰的一部分，然后再向右移动一步，它再次检测到它。然后，您将继续在整个图像中滑动 CNN，查看所有 3 × 3 区域。此外，由于对象可以有不同的大小，您还可以在不同大小的区域之间滑动 CNN。例如，一旦您完成了 3 × 3 区域，您可能还想在所有 4 × 4 区域上滑动 CNN。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "injured-outreach",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtlb5d5pdjj61du0s6tg402.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "satisfied-ground",
   "metadata": {},
   "source": [
    "这种技术相当简单，但正如您所见，它会在略有不同的位置多次检测同一个对象。然后将需要一些后处理来摆脱所有不必要的边界框。一种常见的方法称为非最大抑制。以下是您的操作方法：\n",
    "1. 首先，您需要向 CNN 添加额外的对象性输出，以估计图像中确实存在花的概率（或者，您可以添加“无花”类，但这通常不起作用好）。它必须使用 sigmoid 激活函数，您可以使用二元交叉熵损失来训练它。然后去掉所有对象性分数低于某个阈值的边界框：这将删除所有实际上不包含花的边界框。\n",
    "2. 找到objectness score最高的bounding box，去掉所有与其重叠的bounding box（例如IoU大于60%）。例如，在图 14-24 中，具有最大对象性分数的边界框是最顶部玫瑰上的厚边界框（对象性分数由边界框的厚度表示）。同一朵玫瑰上的另一个边界框与最大边界框重叠很多，因此我们将去掉它。\n",
    "3. 重复第二步，直到没有更多的边界框要摆脱。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bigger-bowling",
   "metadata": {},
   "source": [
    "这种简单的物体检测方法效果很好，但它需要多次运行 CNN，所以速度很慢。 幸运的是，有一种更快的方法可以在图像上滑动 CNN：使用完全卷积网络 (FCN)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sublime-reset",
   "metadata": {},
   "source": [
    "### 全卷积网络"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "massive-highlight",
   "metadata": {},
   "source": [
    "FCN 的想法在 2015 年由 Jonathan Long 等人发表的一篇论文中首次引入，用于语义分割（根据图像所属的对象类别对图像中的每个像素进行分类的任务）。作者指出，您可以用卷积层替换 CNN 顶部的密集层。为了理解这一点，让我们看一个例子：假设一个具有 200 个神经元的密集层位于输出 100 个特征图的卷积层之上，每个特征图的大小为 7 × 7（这是特征图的大小，而不是内核大小）。每个神经元将计算来自卷积层的所有 100 × 7 × 7 激活的加权和（加上偏置项）。现在让我们看看如果我们使用 200 个过滤器（每个过滤器的大小为 7 × 7）和“有效”填充将密集层替换为卷积层会发生什么。该层将输出 200 个特征图，每个 1 × 1（因为内核恰好是输入特征图的大小，并且我们使用了“有效”填充）。换句话说，它会输出 200 个数字，就像密集层一样；如果您仔细观察卷积层执行的计算，您会注意到这些数字与密集层产生的数字完全相同。唯一的区别是密集层的输出是形状为 [batch size, 200] 的张量，而卷积层将输出形状为 [batch size, 1, 1, 200] 的张量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dependent-mileage",
   "metadata": {},
   "source": [
    "> 要将密集层转换为卷积层，卷积层中的过滤器数量必须等于密集层中的单元数，过滤器大小必须等于输入特征图的大小，并且必须使用 “valid”填充。 步幅可以设置为 1 或更多，我们很快就会看到。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "presidential-recipe",
   "metadata": {},
   "source": [
    "为什么这很重要？ 好吧，虽然密集层需要特定的输入大小（因为它每个输入特征有一个权重），但卷积层会很乐意处理任何大小的图像（但是，它确实希望其输入具有特定数量的通道，"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "possible-launch",
   "metadata": {},
   "source": [
    "为什么这很重要？ 好吧，虽然密集层需要特定的输入大小（因为它每个输入特征有一个权重），但卷积层会很乐意处理任何大小的图像（但是，它确实希望其输入具有特定数量的通道，因为每个 内核为每个输入通道包含一组不同的权重）。 由于 FCN 仅包含卷积层（和池化层，它们具有相同的属性），因此可以在任何大小的图像上进行训练和执行！"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "imported-authority",
   "metadata": {},
   "source": [
    "例如，假设我们已经训练了一个用于花卉分类和定位的 CNN。 它在 224 × 224 图像上训练，并输出 10 个数字：输出 0 到 4 通过 softmax 激活函数发送，这给出了类概率（每个类一个）； 输出 5 通过逻辑激活函数发送，这给出了对象性分数； 输出 6 到 9 不使用任何激活函数，它们表示边界框的中心坐标，以及它的高度和宽度。 我们现在可以将其密集层转换为卷积层。 事实上，我们甚至不需要重新训练它； 我们可以将权重从密集层复制到卷积层！ 或者，我们可以在训练之前将 CNN 转换为 FCN。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "expired-nicholas",
   "metadata": {},
   "source": [
    "现在假设输出层（也称为瓶颈层）之前的最后一个卷积层在网络输入 224 × 224 图像时输出 7 × 7 特征图（见图 14-25 左侧）。如果我们向 FCN 提供 448 × 448 图像（参见图 14 25 的右侧），瓶颈层现在将输出 14 × 14 特征图。 27 由于密集输出层被使用 10 个大小过滤器的卷积层取代7 × 7，具有“有效”填充和步长 1，输出将由 10 个特征图组成，每个特征图的大小为 8 × 8（因为 14 – 7 + 1 = 8）。换句话说，FCN 将只处理整个图像一次，它会输出一个 8 × 8 的网格，其中每个单元格包含 10 个数字（5 个类别概率、1 个对象得分和 4 个边界框坐标）。这就像使用原始 CNN 并使用每行 8 步和每列 8 步在图像上滑动一样。为了可视化这一点，想象一下将原始图像切成 14 × 14 的网格，然后在这个网格上滑动一个 7 × 7 的窗口；窗口将有 8 × 8 = 64 个可能的位置，因此有 8 × 8 个预测。然而，FCN 方法效率更高，因为网络只查看图像一次。事实上，You Only Look Once (YOLO) 是一种非常流行的对象检测架构的名称，我们将在接下来介绍。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "reliable-liverpool",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtlbiksd76j610s0tgjv102.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "super-yellow",
   "metadata": {},
   "source": [
    "### 你只看一次（YOLO）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "arctic-impression",
   "metadata": {},
   "source": [
    "YOLO 是 Joseph Redmon 等人提出的一种极其快速和准确的物体检测架构。 在 2015 年的一篇论文中，随后在 2016 年 (YOLOv2) 和 2018 年 (YOLOv3) 中进行了改进。 正如 Redmon 的演示中所见，它非常快，可以在视频上实时运行。\n",
    "\n",
    "YOLOv3 的架构与我们刚刚讨论的架构非常相似，但有一些重要的区别：\n",
    "* 它为每个网格单元输出五个边界框（而不是一个），并且每个边界框都带有一个对象性分数。 它还为每个网格单元输出 20 个类别概率，因为它是在包含 20 个类别的 PASCAL VOC 数据集上训练的。 每个网格单元总共有 45 个数字：5 个边界框，每个边界框有 4 个坐标，加上 5 个对象分数，加上 20 个类别概率。\n",
    "* YOLOv3 不是预测边界框中心的绝对坐标，而是预测相对于网格单元坐标的偏移量，其中 (0, 0) 表示该单元格的左上角，(1, 1) 表示右下角。 对于每个网格单元，YOLOv3 被训练为仅预测中心位于该单元的边界框（但边界框本身通常远远超出网格单元）。 YOLOv3 将逻辑激活函数应用于边界框坐标以确保它们保持在 0 到 1 的范围内。\n",
    "* 在训练神经网络之前，YOLOv3 找到五个有代表性的边界框维度，称为锚框（或边界框先验）。它通过将 K-Means 算法（参见第 9 章）应用于训练集边界框的高度和宽度来实现这一点。例如，如果训练图像包含许多行人，那么其中一个锚框可能具有典型行人的尺寸。然后当神经网络预测每个网格单元有五个边界框时，它实际上预测了每个锚框的重新缩放比例。例如，假设一个锚框高 100 像素，宽 50 像素，并且网络预测，例如，垂直缩放因子为 1.5，水平缩放因子为 0.9（对于网格单元之一）。这将产生大小为 150 × 45 像素的预测边界框。更准确地说，对于每个网格单元和每个锚框，网络预测垂直和水平缩放因子的对数。拥有这些先验使网络更有可能预测适当维度的边界框，并且它还可以加快训练速度，因为它将更快地了解合理的边界框是什么样子。\n",
    "* 网络使用不同尺度的图像进行训练：在训练过程中每隔几批，网络随机选择一个新的图像维度（从 330 × 330 到 608 × 608 像素）。这允许网络学习检测不同尺度的对象。此外，它还可以在不同尺度上使用 YOLOv3：较小尺度的准确度较低，但比较大尺度更快，因此您可以为您的用例选择正确的权衡。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ongoing-coffee",
   "metadata": {},
   "source": [
    "还有一些您可能感兴趣的创新，例如使用跳过连接来恢复在 CNN 中丢失的一些空间分辨率（当我们查看语义分割时，我们将很快讨论这个问题）。 在 2016 年的论文中，作者介绍了使用层次分类的 YOLO9000 模型：该模型预测一个称为 WordTree 的视觉层次结构中每个节点的概率。 这使得网络可以高可信度地预测图像代表一只狗，即使它不确定是什么特定类型的狗。 我鼓励您继续阅读所有三篇论文：它们读起来非常愉快，并且它们提供了如何逐步改进深度学习系统的极好例子。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pressed-driving",
   "metadata": {},
   "source": [
    "**平均平均精度 (mAP)**\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "third-inclusion",
   "metadata": {},
   "source": [
    "对象检测任务中使用的一个非常常见的指标是平均平均精度 (mAP)。 “平均平均值”听起来有点多余，不是吗？ 为了理解这个指标，让我们回到我们在第 3 章中讨论的两个分类指标：准确率和召回率。 记住权衡：召回率越高，精度越低。 您可以在精度/召回曲线中对此进行可视化（参见图 3-5）。 要将这条曲线总结为一个数字，我们可以计算其曲线下面积 (AUC)。 但请注意，精确率/召回率曲线可能包含一些部分，当召回率增加时，精确率实际上会上升，尤其是在召回率较低的情况下（您可以在图 3-5 的左上角看到这一点）。 这是 mAP 指标的动机之一。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "junior-reading",
   "metadata": {},
   "source": [
    "假设分类器在 10% 的召回率下有 90% 的准确率，但在 20% 的召回率下有 96% 的准确率。 这里真的没有权衡：在 20% 的召回率而不是 10% 的召回率下使用分类器更有意义，因为您将获得更高的召回率和更高的精度。 因此，我们不应着眼于 10% 召回率时的精度，而应该着眼于分类器在至少 10% 召回率下可以提供的最大精度。 它将是 96%，而不是 90%。 因此，获得模型性能的一个公平概念的一种方法是计算您可以获得的最大精度，至少为 0%，然后是 10%，20%，依此类推，直到 100%，然后计算平均值 这些最大精度。 这称为平均精度 (AP) 指标。 现在当有两个以上的类时，我们可以计算每个类的 AP，然后计算平均 AP（mAP）。 仅此而已！"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "impressive-interference",
   "metadata": {},
   "source": [
    "在物体检测系统中，还有一个额外的复杂度：如果系统检测到正确的类别，但在错误的位置（即边界框完全关闭）怎么办？ 当然，我们不应将此视为积极的预测。 一种方法是定义一个 IOU 阈值：例如，我们可以认为只有当 IOU 大于 0.5 并且预测的类别是正确的时，预测才是正确的。 相应的 mAP 通常记为 mAP@0.5（或 mAP@50%，有时只是 AP50）。 在一些比赛中（例如 PASCAL VOC 挑战赛），就是这样做的。 在其他比赛中（例如 COCO 比赛），mAP 是针对不同的 IOU 阈值（0.50、0.55、0.60、……、0.95）计算的，最终指标是所有这些 mAP 的平均值（注意 AP@[.50:. 95] 或 AP@[.50:0.05:.95]）。 是的，这是一个平均平均值。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "essential-thursday",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "rental-adolescent",
   "metadata": {},
   "source": [
    "GitHub 上提供了几个使用 TensorFlow 构建的 YOLO 实现。 特别是，查看 Zihao Zang 的 TensorFlow 2 实现。 TensorFlow 模型项目中提供了其他对象检测模型，其中许多具有预训练权重； 有的甚至已经移植到了TF Hub，比如SSD31和Faster RCNN，这两款都非常流行。 SSD也是一种“单发”检测模型，类似于YOLO。 Faster R-CNN 更复杂：图像首先通过 CNN，然后将输出传递给区域建议网络 (RPN)，该网络提出最有可能包含对象的边界框，并为每个边界运行一个分类器 框，基于 CNN 的裁剪输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "israeli-burner",
   "metadata": {},
   "source": [
    "检测系统的选择取决于许多因素：速度、准确性、可用的预训练模型、训练时间、复杂性等。论文包含指标表，但测试环境存在很大的可变性，技术也在不断发展，因此 很快，很难做出一个对大多数人有用且有效期超过几个月的公平比较。\n",
    "\n",
    "因此，我们可以通过在对象周围绘制边界框来定位对象。 很好！ 但也许你想要更精确一点。 让我们看看如何下降到像素级别。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "outer-intellectual",
   "metadata": {},
   "source": [
    "## 语义分割"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "internal-latest",
   "metadata": {},
   "source": [
    "在语义分割中，每个像素根据其所属对象的类别（例如，道路、汽车、行人、建筑物等）进行分类，如图 14-26 所示。注意同一类的不同对象是不区分的。例如，分割图像右侧的所有自行车最终都是一大块像素。该任务的主要难点在于，当图像通过常规 CNN 时，它们逐渐失去其空间分辨率（由于步长大于 1 的层）；所以，一个普通的 CNN 最终可能会知道图像左下角的某个地方有一个人，但它不会比这更精确。\n",
    "\n",
    "就像对象检测一样，有许多不同的方法可以解决这个问题，有些方法非常复杂。然而，Jonathan Long 等人在 2015 年的论文中提出了一个相当简单的解决方案。我们之前讨论过。作者首先采用预训练的 CNN 并将其转换为 FCN。 CNN 将 32 的总步长应用于输入图像（即，如果将所有步长加起来大于 1），这意味着最后一层输出的特征图比输入图像小 32 倍。这显然太粗糙了，所以他们添加了一个上采样层，将分辨率乘以 32。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "musical-buddy",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtlccbbdeej619c0l2n2002.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "approximate-contributor",
   "metadata": {},
   "source": [
    "有几种可用于上采样（增加图像大小）的解决方案，例如双线性插值，但仅适用于 ×4 或 ×8。 相反，他们使用转置卷积层：它相当于首先通过插入空行和列（全零）来拉伸图像，然后执行常规卷积（见图 14-27）。 或者，有些人更喜欢将其视为使用分数步长的常规卷积层（例如，图 14-27 中的 1/2）。 转置卷积层可以初始化为执行接近线性插值的操作，但由于它是一个可训练层，它会在训练过程中学习做得更好。 在 tf.keras 中，可以使用 Conv2DTranspose 层。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "limited-warrior",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtlcdubxrcj61a20m0ad102.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "blind-session",
   "metadata": {},
   "source": [
    "> 在转置卷积层中，步幅定义了输入将被拉伸的程度，而不是滤波器步长的大小，因此步幅越大，输出越大（与卷积层或池化层不同）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "framed-thirty",
   "metadata": {},
   "source": [
    "*TensorFlow 卷积运算*\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eligible-defendant",
   "metadata": {},
   "source": [
    "TensorFlow 还提供了一些其他类型的卷积层：\n",
    "* keras.layers.Conv1D。\n",
    "为一维输入创建卷积层，例如时间序列或文本（字母或单词的序列），我们将在第 15 章中看到。\n",
    "* keras.layers.Conv3D\n",
    "为 3D 输入创建卷积层，例如 3D PET 扫描。\n",
    "* dilation_rate\n",
    "将任何卷积层的 dilation_rate 超参数设置为 2 或更大的值会创建一个 à-trous 卷积层（“à trous”是法语中“有孔”的意思）。这等效于使用带有通过插入零行和零列（即孔）而扩张的过滤器的常规卷积层。例如，一个等于 [[1,2,3]] 的 1 × 3 过滤器可能会以 4 的扩张率进行扩张，从而得到一个 [[1, 0, 0, 0, 2, 0, 0 的扩张过滤器, 0, 3]]。这让卷积层在没有计算成本和不使用额外参数的情况下拥有更大的感受野。\n",
    "* tf.nn.depthwise_conv2d()\n",
    "可用于创建深度卷积层（但您需要自己创建变量）。它独立地将每个过滤器应用于每个单独的输入通道。因此，如果有 $f_n$ 个过滤器和 $f_{n'}$ 个输入通道，那么这将输出 $f_n × f_n'$ 个特征图。\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dangerous-listing",
   "metadata": {},
   "source": [
    "这个解决方案是好的，但仍然太不精确。 为了做得更好，作者添加了来自较低层的跳过连接：例如，他们将输出图像上采样了 2 倍（而不是 32），并且他们添加了具有这种双分辨率的较低层的输出。 然后他们将结果上采样 16 倍，导致总上采样因子为 32（见图 14-28）。 这恢复了早期池化层中丢失的一些空间分辨率。 在他们最好的架构中，他们使用了第二个类似的跳过连接来从更低的层恢复更精细的细节。 简而言之，原始CNN的输出经过以下额外步骤：upscale ×2，添加较低层（适当比例）的输出，upscale ×2，添加更低层的输出，最后upscale ×8. 甚至可以放大到超过原始图像的大小：这可以用来增加图像的分辨率，这是一种称为超分辨率的技术。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "backed-characterization",
   "metadata": {},
   "source": [
    "![](https://tva1.sinaimg.cn/large/008i3skNgy1gtlcjhbn50j615i0f40vi02.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "upper-polyester",
   "metadata": {},
   "source": [
    "再一次，许多 GitHub 存储库提供了语义分割的 TensorFlow 实现（目前为 TensorFlow 1），您甚至可以在 TensorFlow 模型项目中找到预训练的实例分割模型。 实例分割类似于语义分割，但不是将同一类的所有对象合并成一个大块，而是将每个对象与其他对象区分开来（例如，它识别每辆自行车）。 目前，TensorFlow Models 项目中可用的实例分割模型基于 Mask R-CNN 架构，该架构在 2017 年的一篇论文中提出：它通过为每个边界框额外生成一个像素掩码来扩展 Faster R-CNN 模型。 因此，您不仅会在每个对象周围获得一个边界框，以及一组估计的类概率，而且还会得到一个像素掩码，用于定位边界框中属于该对象的像素。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "permanent-services",
   "metadata": {},
   "source": [
    "如您所见，深度计算机视觉领域广阔且发展迅速，每年都会出现各种基于卷积神经网络的架构。短短几年内取得的进展令人震惊，研究人员现在专注于越来越难的问题，例如对抗性学习（它试图使网络对旨在愚弄它的图像更具抵抗力）、可解释性（理解为什么网络进行特定的分类）、逼真的图像生成（我们将在第 17 章中讨论）和单次学习（一个系统，它可以在看到一个物体一次后就识别它）。有些人甚至探索了全新的架构，例如 Geoffrey Hinton 的胶囊网络（我在几个视频中介绍了它们，并在笔记本中提供了相应的代码）。现在进入下一章，我们将研究如何使用循环神经网络和卷积神经网络处理时序数据，例如时间序列。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "intended-paint",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:tensorflow]",
   "language": "python",
   "name": "conda-env-tensorflow-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  },
  "toc-autonumbering": true
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
